Translating biological data into Cytoscape using RCy3
Networks offer us a useful way to represent our biological data. But how do we seamlessly translate our data from R into Cytoscape?
There are multiple ways to communicate with Cytoscape programmatically. There are two main complementary portals,cyRest[@cyrest] and Commands, that form the foundation. cyRest transforms Cytoscape in to a REST (Representational State Transfer) enabled service where it essentially listens for events through a predefined port (by default port 1234). The cyRest functionality started as an app add in but has now been incorporated into the main release. Commands, on the other hand, offer a mechanism whereby app developers can expose their functionality to other apps or to user through the command interface. Prior to the implementation of cyRest many of the basic network functions were first available as commands so there is some overlap between the two different methods. @ref(fig:cytoscapeRcy3) shows the different ways you can call Cytoscape.
Set up
In order to create networks in Cytoscape from R you need:
- RCy3 - a biocondutor package
if(!"RCy3" %in% installed.packages()){
install.packages("BiocManager")
BiocManager::install("RCy3")
}
library(RCy3)
- Cytoscape - Download and install Cytoscape 3.6.1. or higher. Java 9 in not supported. Please make sure that Java 8 is installed.
- Install additional cytoscape apps that will be used in this workshop. If using cytoscape 3.6.1 or older the apps need to manually installed through the app manager in Cytoscape or through your web browser. (click on the method to see detailed instructions)
- Functional Enrichment Collection -a collection of apps to retrieve networks and pathways, integrate and explore the data, perform functional enrichment analysis, and interpret and display your results.
- EnrichmentMap Pipeline Collection - a collection of apps including EnrichmentMap[@enrichmentmap], AutoAnnotate[@autoannotate], WordCloud[@wordcloud] and clusterMaker2[@clustermaker] used to visualize and analysis enrichment results.
If you are using Cytoscape 3.7 or higher then apps can be installed directly from R.
#available in Cytoscape 3.7.0 and above
installApp('STRINGapp')
installApp('aMatReader')
installApp('clusterMaker2')
Make sure that Cytoscape is running
Getting started
Confirm that Cytoscape is installed and opened
Browse available functions, commands and arguments
Depending on what apps you have installed there is different functionality available.
To see all the functions available in RCy3 package
Open swagger docs for live instances of CyREST API. The CyREST API list all the functions available in a base distribution of cytoscape. The below command will launch the swagger documentation in a web browser. Functions are clustered into categories. Expanding individual categories will show all the option available. Further expanding an individual command will show detailed documentation for the function, input, outputs and allow you to try and run the function. Running the function will show the url used for the query and all returned responses.
As mentioned above, there are two ways to interact with Cytoscape, through the Cyrest API or commands. To see the available commands in swagger similar to the Cyrest API.
commandsAPI() # Commands API
To get information about an individual command from the R environment you can also use the commandsHelp function. Simply specify what command you would like to get information on by adding its name to the command. For example “commandsHelp(”help string“)”
Cytoscape Basics
Create a Cytoscape network from some basic R objects
nodes <- data.frame(id=c("node 0","node 1","node 2","node 3"),
group=c("A","A","B","B"), # categorical strings
score=as.integer(c(20,10,15,5)), # integers
stringsAsFactors=FALSE)
edges <- data.frame(source=c("node 0","node 0","node 0","node 2"),
target=c("node 1","node 2","node 3","node 3"),
interaction=c("inhibits","interacts","activates","interacts"), # optional
weight=c(5.1,3.0,5.2,9.9), # numeric
stringsAsFactors=FALSE)
createNetworkFromDataFrames(nodes,edges, title="my first network", collection="DataFrame Example")
Remember. All networks we make are created in Cytoscape so get an image of the resulting network and include it in your current analysis if desired. We can define a filename to export to, in the current directory.
initial_network_png_file_name <- file.path(getwd(),"initial_example_network.png")
if(file.exists(initial_network_png_file_name)){
#cytoscape hangs waiting for user response if file already exists. Remove it first
file.remove(initial_network_png_file_name)
}
#export the network
exportImage(filename="initial_example_network.png", type = "png")
Example Data Set
We downloaded gene expression data from the Ovarian Serous Cystadenocarcinoma project of The Cancer Genome Atlas (TCGA)[@TCGA], http://cancergenome.nih.gov via the Genomic Data Commons (GDC) portal[@GDC] on 2017-06-14 using TCGABiolinks R package[@TCGABiolinks]. The data includes 300 samples available as RNA-seq data, with reads mapped to a reference genome using MapSplice[@MapSplice] and read counts per transcript determined using the RSEM method[@RSEM]. RNA-seq data are labeled as ‘RNA-Seq V2’, see details at: https://wiki.nci.nih.gov/display/TCGA/RNASeq+Version+2). The RNA-SeqV2 data consists of raw counts similar to regular RNA-seq but RSEM (RNA-Seq by Expectation Maximization) data can be used with the edgeR method. The expression dataset of 300 tumours, with 79 classified as Immunoreactive, 72 classified as Mesenchymal, 69 classified as Differentiated, and 80 classified as Proliferative samples(class definitions were obtained from Verhaak et al.[@OV] Supplementary Table 1, third column). RNA-seq read counts were converted to CPM values and genes with CPM > 1 in at least 50 of the samples are retained for further study (50 is the minimal sample size in the classes). The data was normalized and differential expression was calculated for each cancer class relative to the rest of the samples.
There are two data files: 1. Expression matrix - containing the normalized expression for each gene across all 300 samples. 1. Gene ranks - containing the p-values, FDR and foldchange values for the 4 comparisons (mesenchymal vs rest, differential vs rest, proliferative vs rest and immunoreactive vs rest)
The following commands will create a new directory in your current directory, and download the data files to it.
#create a source directory for the download
dir.create(file.path(getwd(),"230_Isserlin_RCy3_intro"))
#define locations for download
url_exp <- "https://raw.githubusercontent.com/cytoscape/cytoscape-tutorials/gh-pages/presentations/modules/RCy3_ExampleData/data/TCGA_OV_RNAseq_expression.txt"
path_exp <- file.path(getwd(),"230_Isserlin_RCy3_intro", "TCGA_OV_RNAseq_expression.txt")
url_scores <-"https://github.com/cytoscape/cytoscape-tutorials/blob/gh-pages/presentations/modules/RCy3_ExampleData/data/TCGA_OV_RNAseq_All_edgeR_scores.txt?raw=true"
path_scores <- file.path(getwd(),"230_Isserlin_RCy3_intro", "TCGA_OV_RNAseq_All_edgeR_scores.txt")
#download files
download.file(url_exp, path_exp)
download.file(url_scores, path_scores)
Now we can read the downloaded files into R:
RNASeq_expression_matrix <- read.table(path_exp, header = TRUE, sep = "\t", quote="\"", stringsAsFactors = FALSE)
RNASeq_gene_scores <- read.table(path_scores, header = TRUE, sep = "\t", quote="\"", stringsAsFactors = FALSE)
Finding Network Data
How do I represent my data as a network?
Unfortunately, there is not a simple answer. It depends on your biological question!
Example use cases:
- Omics data - I have a fill in the blank (microarray, RNASeq, Proteomics, ATACseq, MicroRNA, GWAS …) dataset. I have normalized and scored my data. How do I overlay my data on existing interaction data?
- Coexpression data - I have a dataset that represents relationships. How do I represent it as a network.
Use Case 1 - How are my top genes related?
Omics data - I have a fill in the blank (microarray, RNASeq, Proteomics, ATACseq, MicroRNA, GWAS …) dataset. I have normalized and scored my data. How do I overlay my data on existing interaction data?
There are endless amounts of databases storing interaction data.
Thankfully we don’t have to query each independent ally. In addition to many specialized (for example, for specific molecules, interaction type, or species) interaction databases there are also databases that collate these databases to create a broad resource that is easier to use. For example:
- StringApp - is a protein - protein and protein- chemical database that imports data from String[@string] (which itself includes data from multiple species, coexpression, text-mining,existing databases, and genomic context), [STITCH] into a unified, queriable database.
- PSICQUIC[@psicquic] - a REST-ful service that is the responsibility of the database provider to set up and maintain. PSICQUIC is an additional interface that allows users to search all available databases (that support this REST-ful service). The databases are required to represent their interaction data in Proteomic Standards Initiative - molecular interaction (PSI-MI) format. To see a list of all the available data source see here
- nDex[@ndex] - a network data exchange repository.
- GeneMANIA[@genemania] - contains multiple networks (shared domains, physical interactions, pathways, predicted, co-expression, genetic interactions and co-localized network). Given a set of genes GeneMANIA selects and weights networks that optimize the connectivity between the query genes. GeneMANIA will also return additional genes that are highly related to your query set.
- PathwayCommons - (access the data through the CyPath2App) is a pathway and interaction data source. Data is collated from a large set of resources (list here ) and stored in the BioPAX[@biopax] format. BioPAX is a data standard that allows for detailed representation of pathway mechanistic details as opposed to collapsing it to the set of interactions between molecules. BioPAX pathways from Pathway commons can also be loaded directly into R using the PaxToolsR[@paxtoolsr] Bioconductor package.
Get a subset of genes of interest from our scored data:
top_mesenchymal_genes <- RNASeq_gene_scores[which(RNASeq_gene_scores$FDR.mesen < 0.05 & RNASeq_gene_scores$logFC.mesen > 2),]
head(top_mesenchymal_genes)
We are going to query the String Database to get all interactions found for our set of top Mesenchymal genes.
Reminder: to see the parameters required by the string function or to find the right string function you can use commandsHelp.
commandsHelp("help string")
commandsHelp("help string protein query")
mesen_string_interaction_cmd <- paste('string protein query taxonID=9606 limit=150 cutoff=0.9 query="',paste(top_mesenchymal_genes$Name, collapse=","),'"',sep="")
commandsGET(mesen_string_interaction_cmd)
Get a screenshot of the initial network
initial_string_network_png_file_name <- file.path(getwd(),"230_Isserlin_RCy3_intro", "initial_string_network.png")
if(file.exists(initial_string_network_png_file_name)){
#cytoscape hangs waiting for user response if file already exists. Remove it first
response <- file.remove(initial_string_network_png_file_name)
}
response <- exportImage(initial_string_network_png_file_name, type = "png")
Layout the network
layoutNetwork('force-directed')
Check what other layout algorithms are available to try out
Get the parameters for a specific layout
getLayoutPropertyNames(layout.name='force-directed')
Re-layout the network using the force directed layout but specify some of the parameters
layoutNetwork('force-directed defaultSpringCoefficient=0.0000008 defaultSpringLength=70')
Get a screenshot of the re-laid out network
relayout_string_network_png_file_name <- file.path(getwd(),"230_Isserlin_RCy3_intro", "relayout_string_network.png")
if(file.exists(relayout_string_network_png_file_name)){
#cytoscape hangs waiting for user response if file already exists. Remove it first
response<- file.remove(relayout_string_network_png_file_name)
}
response <- exportImage(relayout_string_network_png_file_name, type = "png")
Overlay our expression data on the String network.
To do this we will be using the loadTableData function from RCy3. It is important to make sure that that your identifiers types match up. You can check what is used by String by pulling in the column names of the node attribute table.
getTableColumnNames('node')
If you are unsure of what each column is and want to further verify the column to use you can also pull in the entire node attribute table.
node_attribute_table_topmesen <- getTableColumns(table="node")
head(node_attribute_table_topmesen[,3:7])
The column “display name” contains HGNC gene names which are also found in our Ovarian Cancer dataset.
To import our expression data we will match our dataset to the “display name” node attribute.
?loadTableData
loadTableData(RNASeq_gene_scores,table.key.column = "display name",data.key.column = "Name") #default data.frame key is row.names
Modify the Visual Style Create your own visual style to visualize your expression data on the String network.
Start with a default style
style.name = "MesenchymalStyle"
defaults.list <- list(NODE_SHAPE="ellipse",
NODE_SIZE=60,
NODE_FILL_COLOR="#AAAAAA",
EDGE_TRANSPARENCY=120)
node.label.map <- mapVisualProperty('node label','display name','p') # p for passthrough; nothing else needed
createVisualStyle(style.name, defaults.list, list(node.label.map))
setVisualStyle(style.name=style.name)
Update your created style with a mapping for the Mesenchymal logFC expression. The first step is to grab the column data from Cytoscape (we can reuse the node_attribute table concept from above but we have to call the function again as we have since added our expression data) and pull out the min and max to define our data mapping range of values.
Note: you could define the min and max based on the entire dataset or just the subset that is represented in Cytoscape currently. The two methods will give you different results. If you intend on comparing different networks created with the same dataset then it is best to calculate the min and max from the entire dataset as opposed to a subset.
min.mesen.logfc = min(RNASeq_gene_scores$logFC.mesen,na.rm=TRUE)
max.mesen.logfc = max(RNASeq_gene_scores$logFC.mesen,na.rm=TRUE)
data.values = c(min.mesen.logfc,0,max.mesen.logfc)
Next, we use the RColorBrewer package to help us pick good colors to pair with our data values.
library(RColorBrewer)
display.brewer.all(length(data.values), colorblindFriendly=TRUE, type="div") # div,qual,seq,all
node.colors <- c(rev(brewer.pal(length(data.values), "RdBu")))
Map the colors to our data value and update our visual style.
setNodeColorMapping("logFC.mesen", data.values, node.colors, style.name=style.name)
Remember, String includes your query proteins as well as other proteins that associate with your query proteins (including the strongest connection first). Not all of the proteins in this network are your top hits. How can we visualize which proteins are our top Mesenchymal hits?
Add a different border color or change the node shape for our top hits.
getNodeShapes()
#select the Nodes of interest
#selectNode(nodes = top_mesenchymal_genes$Name, by.col="display name")
setNodeShapeBypass(node.names = top_mesenchymal_genes$Name, new.shapes = "TRIANGLE")
Change the size of the node to be correlated with the Mesenchymal p-value.
setNodeSizeMapping(table.column = 'LR.mesen',
table.column.values = c(min(RNASeq_gene_scores$LR.mesen),
mean(RNASeq_gene_scores$LR.mesen),
max(RNASeq_gene_scores$LR.mesen)),
sizes = c(30, 60, 150),mapping.type = "c", style.name = style.name)
Get a screenshot of the resulting network
mesen_string_network_png_file_name <- file.path(getwd(),"230_Isserlin_RCy3_intro", "mesen_string_network.png")
if(file.exists(mesen_string_network_png_file_name)){
#cytoscape hangs waiting for user response if file already exists. Remove it first
response<- file.remove(mesen_string_network_png_file_name)
}
response <- exportImage(mesen_string_network_png_file_name, type = "png")
Use Case 2 - Which genes have similar expression.
Instead of querying existing resources look for correlations in your own dataset to find out which genes have similar expression. There are many tools that can analyze your data for correlation. A popular tool is Weighted Gene Correlation Network Analysis (WGCNA)[@wgcna] which takes expression data and calculates functional modules. As a simple example we can transform our expression dataset into a correlation matrix.
Using the Cytoscape App, aMatReader[@amatreader], we transform our adjacency matrix into an interaction network. First we filter the correlation matrix to contain only the strongest connections (for example, only correlations greater than 0.9).
RNASeq_expression <- RNASeq_expression_matrix[,3:ncol(RNASeq_expression_matrix)]
rownames(RNASeq_expression) <- RNASeq_expression_matrix$Name
RNAseq_correlation_matrix <- cor(t(RNASeq_expression), method="pearson")
#set the diagonal of matrix to zero - eliminate self-correlation
RNAseq_correlation_matrix[
row(RNAseq_correlation_matrix) == col(RNAseq_correlation_matrix) ] <- 0
# set all correlations that are less than 0.9 to zero
RNAseq_correlation_matrix[which(RNAseq_correlation_matrix<0.90)] <- 0
#get rid of rows and columns that have no correlations with the above thresholds
RNAseq_correlation_matrix <- RNAseq_correlation_matrix[which(rowSums(RNAseq_correlation_matrix) != 0),
which(colSums(RNAseq_correlation_matrix) !=0)]
#write out the correlation file
correlation_filename <- file.path(getwd(), "230_Isserlin_RCy3_intro", "TCGA_OV_RNAseq_expression_correlation_matrix.txt")
write.table(RNAseq_correlation_matrix, file = correlation_filename, col.names = TRUE, row.names = FALSE, sep = "\t", quote=FALSE)
Use the CyRest call to access the aMatReader functionality.
amat_url <- "aMatReader/v1/import"
amat_params = list(files = list(correlation_filename),
delimiter = "TAB",
undirected = FALSE,
ignoreZeros = TRUE,
interactionName = "correlated with",
rowNames = FALSE
)
response <- cyrestPOST(operation = amat_url, body = amat_params, base.url = "http://localhost:1234")
current_network_id <- response$data["suid"]
#relayout network
layoutNetwork('cose',
network = as.numeric(current_network_id))
renameNetwork(title ="Coexpression_network_pear0_95_new",
network = as.numeric(current_network_id))
Modify the visualization to see where each genes is predominantly expressed. Look at the 4 different p-values associated with each gene and color the nodes with the type associated with the lowest FDR.
Load in the scoring data. Specify the cancer type where the genes has the lowest FDR value.
nodes_in_network <- rownames(RNAseq_correlation_matrix)
#add an additional column to the gene scores table to indicate in which samples
# the gene is significant
node_class <- vector(length = length(nodes_in_network),mode = "character")
for(i in 1:length(nodes_in_network)){
current_row <- which(RNASeq_gene_scores$Name == nodes_in_network[i])
min_pvalue <- min(RNASeq_gene_scores[current_row,
grep(colnames(RNASeq_gene_scores), pattern = "FDR")])
if(RNASeq_gene_scores$FDR.mesen[current_row] <=min_pvalue){
node_class[i] <- paste(node_class[i],"mesen",sep = " ")
}
if(RNASeq_gene_scores$FDR.diff[current_row] <=min_pvalue){
node_class[i] <- paste(node_class[i],"diff",sep = " ")
}
if(RNASeq_gene_scores$FDR.prolif[current_row] <=min_pvalue){
node_class[i] <- paste(node_class[i],"prolif",sep = " ")
}
if(RNASeq_gene_scores$FDR.immuno[current_row] <=min_pvalue){
node_class[i] <- paste(node_class[i],"immuno",sep = " ")
}
}
node_class <- trimws(node_class)
node_class_df <-data.frame(name=nodes_in_network, node_class,stringsAsFactors = FALSE)
head(node_class_df)
Map the new node attribute and the all the gene scores to the network.
loadTableData(RNASeq_gene_scores,table.key.column = "name",data.key.column = "Name") #default data.frame key is row.names
loadTableData(node_class_df,table.key.column = "name",data.key.column = "name") #default data.frame key is row.names
Create a color mapping for the different cancer types.
#create a new mapping with the different types
unique_types <- sort(unique(node_class))
coul = brewer.pal(4, "Set1")
# I can add more tones to this palette :
coul = colorRampPalette(coul)(length(unique_types))
setNodeColorMapping(table.column = "node_class",table.column.values = unique_types,
colors = coul,mapping.type = "d")
correlation_network_png_file_name <- file.path(getwd(),"230_Isserlin_RCy3_intro", "correlation_network.png")
if(file.exists(correlation_network_png_file_name)){
#cytoscape hangs waiting for user response if file already exists. Remove it first
file.remove(correlation_network_png_file_name)
}
#export the network
exportImage(correlation_network_png_file_name, type = "png")
cluster the Network
#make sure it is set to the right network
setCurrentNetwork(network = getNetworkName(suid=as.numeric(current_network_id)))
#cluster the network
clustermaker_url <- paste("cluster mcl network=SUID:",current_network_id, sep="")
commandsGET(clustermaker_url)
#get the clustering results
default_node_table <- getTableColumns(table= "node",network = as.numeric(current_network_id))
head(default_node_table)
Perform pathway Enrichment on one of the clusters using g:Profiler[@gprofiler]. g:Profiler is an online functional enrichment web service that will take your gene list and return the set of enriched pathways. For automated analysis g:Profiler has created an R library to interact with it directly from R instead of using the web page.
Create a function to call g:Profiler and convert the returned results into a generic enrichment map input file.
tryCatch(expr = { library("gProfileR")},
error = function(e) { install.packages("gProfileR")}, finally = library("gProfileR"))
#function to run gprofiler using the gprofiler library
#
# The function takes the returned gprofiler results and formats it to the generic EM input file
#
# function returns a data frame in the generic EM file format.
runGprofiler <- function(genes,current_organism = "hsapiens",
significant_only = F, set_size_max = 200,
set_size_min = 3, filter_gs_size_min = 5 , exclude_iea = F){
gprofiler_results <- gprofiler(genes ,
significant=significant_only,ordered_query = F,
exclude_iea=exclude_iea,max_set_size = set_size_max,
min_set_size = set_size_min,
correction_method = "fdr",
organism = current_organism,
src_filter = c("GO:BP","REAC"))
#filter results
gprofiler_results <- gprofiler_results[which(gprofiler_results[,'term.size'] >= 3
& gprofiler_results[,'overlap.size'] >= filter_gs_size_min ),]
# gProfileR returns corrected p-values only. Set p-value to corrected p-value
if(dim(gprofiler_results)[1] > 0){
em_results <- cbind(gprofiler_results[,
c("term.id","term.name","p.value","p.value")], 1,
gprofiler_results[,"intersection"])
colnames(em_results) <- c("Name","Description", "pvalue","qvalue","phenotype","genes")
return(em_results)
} else {
return("no gprofiler results for supplied query")
}
}
Run g:Profiler. g:Profiler will return a set of pathways and functions that are found to be enriched in our query set of genes.
current_cluster <- "1"
#select all the nodes in cluster 1
selectednodes <- selectNodes(current_cluster, by.col="__mclCluster")
#create a subnetwork with cluster 1
subnetwork_suid <- createSubnetwork(nodes="selected")
renameNetwork("Cluster1_Subnetwork", network=as.numeric(subnetwork_suid))
subnetwork_node_table <- getTableColumns(table= "node",network = as.numeric(subnetwork_suid))
em_results <- runGprofiler(subnetwork_node_table$name)
#write out the g:Profiler results
em_results_filename <-file.path(getwd(),
"230_Isserlin_RCy3_intro",paste("gprofiler_cluster",current_cluster,"enr_results.txt",sep="_"))
write.table(em_results,em_results_filename,col.name=TRUE,sep="\t",row.names=FALSE,quote=FALSE)
head(em_results)
Create an enrichment map with the returned g:Profiler results. An enrichment map is a different sort of network. Instead of nodes representing genes, nodes represent pathways or functions. Edges between these pathways or functions represent shared genes or pathway crosstalk. An enrichment map is a way to visualize your enrichment results to help reduce redundancy and uncover main themes. Pathways can also be explored in detail using the features available through the App in Cytoscape.
em_command = paste('enrichmentmap build analysisType="generic" ',
'pvalue=',"0.05", 'qvalue=',"0.05",
'similaritycutoff=',"0.25",
'coeffecients=',"JACCARD",
'enrichmentsDataset1=',em_results_filename ,
sep=" ")
#enrichment map command will return the suid of newly created network.
em_network_suid <- commandsRun(em_command)
renameNetwork("Cluster1_enrichmentmap", network=as.numeric(em_network_suid))
Export image of resulting Enrichment map.
cluster1em_png_file_name <- file.path(getwd(),"230_Isserlin_RCy3_intro","cluster1em.png")
if(file.exists(cluster1em_png_file_name)){
#cytoscape hangs waiting for user response if file already exists. Remove it first
file.remove(cluster1em_png_file_name)
}
#export the network
exportImage(cluster1em_png_file_name, type = "png")
Annotate the Enrichment map to get the general themes that are found in the enrichment results of cluster 1
#get the column from the nodetable and node table
nodetable_colnames <- getTableColumnNames(table="node", network = as.numeric(em_network_suid))
descr_attrib <- nodetable_colnames[grep(nodetable_colnames, pattern = "GS_DESCR")]
#Autoannotate the network
autoannotate_url <- paste("autoannotate annotate-clusterBoosted labelColumn=", descr_attrib," maxWords=3 ", sep="")
current_name <-commandsGET(autoannotate_url)
Export image of resulting Annotated Enrichment map.
cluster1em_annot_png_file_name <- file.path(getwd(), "230_Isserlin_RCy3_intro","cluster1em_annot.png")
if(file.exists(cluster1em_annot_png_file_name)){
#cytoscape hangs waiting for user response if file already exists. Remove it first
file.remove(cluster1em_annot_png_file_name)
}
#export the network
exportImage(cluster1em_annot_png_file_name, type = "png")
Dense networks small or large never look like network figures we so often see in journals. A lot of manual tweaking, reorganization and optimization is involved in getting that perfect figure ready network. The above network is what the network starts as. The below figure is what it can look like after a few minutes of manual reorganiazation. (individual clusters were selected from the auto annotate panel and separated from other clusters)
LS0tCnRpdGxlOiAiQ3l0b3NjYXBlIGF1dG9tYXRpb24gaW4gUiB1c2luZyBSY3kzIgphdXRob3I6ICJieSBSdXRoIElzc2VybGluIgpwYWNrYWdlOiBSQ3kzCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6ICJub25lIgojICBwZGZfZG9jdW1lbnQ6CiMgICAgdG9jOiB0cnVlICAKLS0tCmBgYHtyLCBlY2hvID0gRkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBldmFsPUZBTFNFCikKYGBgCgpgYGB7ciBsaWIsIGVjaG89RkFMU0UsIHJlc3VsdHM9ImhpZGUiLCBjYWNoZT1GQUxTRX0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKHsKCWxpYnJhcnkoRW5yaWNobWVudEJyb3dzZXIpCglsaWJyYXJ5KFJDeTMpCglsaWJyYXJ5KFJDb2xvckJyZXdlcikKCWxpYnJhcnkoZ1Byb2ZpbGVSKQp9KQpgYGAKCiMgT3ZlcnZpZXcKCiMjIyBJbnN0cnVjdG9yIG5hbWVzIGFuZCBjb250YWN0IGluZm9ybWF0aW9uCgoqIFJ1dGggSXNzZXJsaW4gLSBydXRoIGRvdCBpc3NlcmxpbiAoYXQpIHV0b3JvbnRvIChkb3QpIGNhCiogQnJlbmRhbiBJbm5lcyAtIGJyZW5kYW4gKGRvdCkgaW5uZXMgKGF0KSBtYWlsIChkb3QpIHV0b3JvbnRvIChkb3QpIGNhCiogSmVmZiBXb25nIC0ganZ3b25nIChhdCkgZ21haWwgKGRvdCkgY29tIAoqIEdhcnkgQmFkZXIgLSBnYXJ5IChkb3QpIGJhZGVyIChhdCkgdXRvcm9udG8gKGRvdCkgY2EKCiMjIyBXb3Jrc2hvcCBEZXNjcmlwdGlvbgoKQ3l0b3NjYXBlKHd3dy5jeXRvc2NhcGUub3JnKSBpcyBvbmUgb2YgdGhlIG1vc3QgcG9wdWxhciBhcHBsaWNhdGlvbnMgZm9yIG5ldHdvcmsgYW5hbHlzaXMgYW5kIHZpc3VhbGl6YXRpb24uIEluIHRoaXMgd29ya3Nob3AsIHdlIHdpbGwgZGVtb25zdHJhdGUgbmV3IGNhcGFiaWxpdGllcyB0byBpbnRlZ3JhdGUgQ3l0b3NjYXBlIGludG8gcHJvZ3JhbW1hdGljIHdvcmtmbG93cyBhbmQgcGlwZWxpbmVzIHVzaW5nIFIuIFdlIHdpbGwgYmVnaW4gd2l0aCBhbiBvdmVydmlldyBvZiBuZXR3b3JrIGJpb2xvZ3kgdGhlbWVzIGFuZCBjb25jZXB0cywgYW5kIHRoZW4gd2Ugd2lsbCB0cmFuc2xhdGUgdGhlc2UgaW50byBDeXRvc2NhcGUgdGVybXMgZm9yIHByYWN0aWNhbCBhcHBsaWNhdGlvbnMuIFRoZSBidWxrIG9mIHRoZSB3b3Jrc2hvcCB3aWxsIGJlIGEgaGFuZHMtb24gZGVtb25zdHJhdGlvbiBvZiBhY2Nlc3NpbmcgYW5kIGNvbnRyb2xsaW5nIEN5dG9zY2FwZSBmcm9tIFIgdG8gcGVyZm9ybSBhIG5ldHdvcmsgYW5hbHlzaXMgb2YgdHVtb3IgZXhwcmVzc2lvbiBkYXRhLgoKIyMjIFdvcmtzaG9wIHByZXJlcXVpc2l0ZXMKKiBCYXNpYyBrbm93bGVkZ2Ugb2YgUiBzeW50YXgKKiBCYXNpYyBrbm93bGVkZ2Ugb2YgQ3l0b3NjYXBlIHNvZnR3YXJlCiogRmFtaWxpYXJpdHkgd2l0aCBuZXR3b3JrIGJpb2xvZ3kgY29uY2VwdHMKCiMjIyBCYWNrZ3JvdW5kCiog4oCcSG93IHRvIHZpc3VhbGx5IGludGVycHJldCBiaW9sb2dpY2FsIGRhdGEgdXNpbmcgbmV0d29ya3Mu4oCdIE1lcmljbyBELCBHZmVsbGVyIEQsIEJhZGVyIEdELiBOYXR1cmUgQmlvdGVjaG5vbG9neSAyMDA5IE9jdCAyNywgOTIxLTkyNCAtIGh0dHA6Ly9iYWRlcmxhYi5vcmcvUHVibGljYXRpb25zP2FjdGlvbj1BdHRhY2hGaWxlJmRvPXZpZXcmdGFyZ2V0PTIwMDlfTWVyaWNvX1ByaW1lcl9OYXRCaW90ZWNoX09jdC5wZGYKKiDigJxDeVJFU1Q6IFR1cmJvY2hhcmdpbmcgQ3l0b3NjYXBlIEFjY2VzcyBmb3IgRXh0ZXJuYWwgVG9vbHMgdmlhIGEgUkVTVGZ1bCBBUEnigJ0uIEtlaWljaGlybyBPbm8sIFRhbmphIE11ZXR6ZSwgR2VvcmdpIEtvbGlzaG92c2tpLCBQYXVsIFNoYW5ub24sIEJhcnJ5IERlbWNoYWsuRjEwMDBSZXMuIDIwMTUgQXVnIDU7NDo0NzguIC0gaHR0cHM6Ly9mMTAwMHJlc2VhcmNoLmNvbS9hcnRpY2xlcy80LTQ3OC92MQoKIyMjIFdvcmtzaG9wIFBhcnRpY2lwYXRpb24KUGFydGljaXBhbnRzIGFyZSByZXF1aXJlZCB0byBicmluZyBhIGxhcHRvcCB3aXRoIEN5dG9zY2FwZSwgUiwgYW5kIFJTdHVkaW8gaW5zdGFsbGVkLiAgSW5zdGFsbGF0aW9uIGluc3RydWN0aW9ucyB3aWxsIGJlIHByb3ZpZGVkIGluIHRoZSB3ZWVrcyBwcmVjZWRpbmcgdGhlIHdvcmtzaG9wLiAgVGhlIHdvcmtzaG9wIHdpbGwgY29uc2lzdCBvZiBhIGxlY3R1cmUgYW5kIGxhYi4KCiMjIyBSIC8gQmlvY29uZHVjdG9yIHBhY2thZ2VzIHVzZWQKKiBSQ3kzCiogZ1Byb2ZpbGVSCgojIyMgVGltZSBvdXRsaW5lCgp8IEFjdGl2aXR5ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBUaW1lIHwKfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS18CnwgSW50cm9kdWN0aW9uICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IDE1bSAgfAp8IERyaXZpbmcgQ3l0b3NjYXBlIGZyb20gUiAgICAgICAgICAgICAgICAgICAgICAgfCAxNW0gIHwKfCBDcmVhdGluZywgcmV0cmlldmluZyBhbmQgbWFuaXB1bGF0aW5nIG5ldHdvcmtzIHwgMTVtICB8CnwgU3VtbWFyeSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IDEwbSAgfAoKCiMjIyBXb3Jrc2hvcCBnb2FscyBhbmQgb2JqZWN0aXZlcwoKTGVhcm5pbmcgZ29hbHM6CiogS25vdyB3aGVuIGFuZCBob3cgdG8gdXNlIEN5dG9zY2FwZSBpbiB5b3VyIHJlc2VhcmNoIGFyZWEKKiBHZW5lcmFsaXplIG5ldHdvcmsgYW5hbHlzaXMgbWV0aG9kcyB0byBtdWx0aXBsZSBwcm9ibGVtIGRvbWFpbnMKKiBJbnRlZ3JhdGUgQ3l0b3NjYXBlIGludG8geW91ciBiaW9pbmZvcm1hdGljcyBwaXBlbGluZXMKCkxlYXJuaW5nIG9iamVjdGl2ZXM6CiogUHJvZ3JhbW1hdGljIGNvbnRyb2wgb3ZlciBDeXRvc2NhcGUgZnJvbSBSCiogUHVibGlzaCwgc2hhcmUgYW5kIGV4cG9ydCBuZXR3b3JrcwoKIyBCYWNrZ3JvdW5kCiMjIEN5dG9zY2FwZQo8ZGl2IGlkPSJsZWZ0X2xvZ28iPiFbQ3l0b3NjYXBlIEFwcHNdKC4vYmlvYzIwMThfUmN5M19pbnRyby8yMzBfSXNzZXJsaW5fUkN5M19pbnRyby9pbWFnZXMvY3kzbG9nb09yYW5nZS5wbmcpe3dpZHRoPTEwMHB4fTwvZGl2PgoKICAqIFtDeXRvc2NhcGVdKHd3dy5jeXRvc2NhcGUub3JnKVtAY3l0b3NjYXBlXSBpcyBhIGZyZWVseSBhdmFpbGFibGUgb3Blbi1zb3VyY2UsIGNyb3NzIHBsYXRmb3JtIG5ldHdvcmsgYW5hbHlzaXMgc29mdHdhcmUuICAKICAqIFtDeXRvc2NhcGVdKHd3dy5jeXRvc2NhcGUub3JnKVtAY3l0b3NjYXBlXSBjYW4gdmlzdWFsaXplIGNvbXBsZXggbmV0d29ya3MgYW5kIGhlbHAgaW50ZWdyYXRlIHRoZW0gd2l0aCBhbnkgb3RoZXIgZGF0YSB0eXBlLiAgCiAgKiBbQ3l0b3NjYXBlXSh3d3cuY3l0b3NjYXBlLm9yZylbQGN5dG9zY2FwZV0gaGFzIGFuIGFjdGl2ZSBkZXZlbG9wZXIgYW5kIHVzZXIgYmFzZSB3aXRoIG1vcmUgdGhhbiAqKjMwMCoqIGNvbW11bml0eSBjcmVhdGVkIGFwcHMgYXZhaWxhYmxlIGZyb20gdGhlIChDeXRvc2NhcGUgQXBwIFN0b3JlKVthcHBzLmN5dG9zY2FwZS5vcmddLgogICogQ2hlY2sgb3V0IHNvbWUgb2YgdGhlIHRhc2tzIHlvdSBjYW4gZG8gd2l0aCBDeXRvc2NhcGUgaW4gb3VyIG9ubGluZSB0dXRvcmlhbCBndWlkZSAtIHR1dG9yaWFscy5jeXRvc2NhcGUub3JnCgojIyBPdmVydmlldyBvZiBuZXR3b3JrIGJpb2xvZ3kgdGhlbWVzIGFuZCBjb25jZXB0cwojIyMgV2h5IE5ldHdvcmtzPwpOZXR3b3JrcyBhcmUgZXZlcnl3aGVyZS4uLgoKICAqIE1vbGVjdWxhciBOZXR3b3JrcwogICogQ2VsbC1DZWxsIGNvbW11bmljYXRpb24gTmV0d29ya3MKICAqIENvbXB1dGVyIG5ldHdvcmtzCiAgKiBTb2NpYWwgTmV0d29ya3MKICAqIEludGVybmV0CiAgCk5ldHdvcmtzIGFyZSBwb3dlcmZ1bCB0b29scy4uLgoKICAqIFJlZHVjZSBjb21wbGV4aXR5CiAgKiBNb3JlIGVmZmljaWVudCB0aGFuIHRhYmxlcwogICogR3JlYXQgZm9yIGRhdGEgaW50ZWdyYXRpb24KICAqIEludHVpdGl2ZSB2aXN1YWxpemF0aW9uCiAgCk9mdGVuIGRhdGEgaW4gb3VyIHBpcGVsaW5lcyBhcmUgcmVwcmVzZW50ZWQgYXMgZGF0YS5mcmFtZXMsIHRhYmxlcywgbWF0cmljZXMsIHZlY3RvcnMgb3IgbGlzdHMuICBTb21ldGltZXMgd2UgcmVwcmVzZW50IHRoaXMgZGF0YSBhcyBoZWF0bWFwcywgb3IgcGxvdHMgaW4gZWZmb3J0cyB0byBzdW1tYXJpemUgdGhlIHJlc3VsdHMgdmlzdWFsbHkuICBOZXR3b3JrIHZpc3VhbGl6YXRpb24gb2ZmZXJzIGFuIGFkZGl0aW9uYWwgbWV0aG9kIHRoYXQgcmVhZGlseSBpbmNvcnBvcmF0ZXMgbWFueSBkaWZmZXJlbnQgZGF0YSB0eXBlcyBhbmQgdmFyaWFibGVzIGludG8gYSBzaW5nbGUgcGljdHVyZS4gCgo8Y2VudGVyPgohW10oaHR0cHM6Ly9jeXRvc2NhcGUuZ2l0aHViLmlvL2N5dG9zY2FwZS1hdXRvbWF0aW9uL2Zvci1zY3JpcHRlcnMvUi9ub3RlYm9va3MvYmlvYzIwMThfUmN5M19pbnRyby8yMzBfSXNzZXJsaW5fUkN5M19pbnRyby9pbWFnZXMvdGFibGVzMm5ldHdvcmtzLnBuZykKPC9jZW50ZXI+CgpJbiBvcmRlciB0byB0cmFuc2xhdGUgeW91ciBkYXRhIGludG8gYSBuZXR3b3JrIGl0IGlzIGltcG9ydGFudCB0byBkZWZpbmUgdGhlIGVudGl0aWVzIGFuZCB0aGVpciByZWxhdGlvbnNoaXBzLiAgRW50aXRpZXMgYW5kIHJlbGF0aW9uc2hpcHMgY2FuIGJlIGFueXRoaW5nLiBUaGV5IGNhbiBiZSB1c2VyIGRlZmluZWQgb3IgdGhleSBjYW4gYmUgcXVlcmllZCBmcm9tIGEgZGF0YWJhc2UuCgpFeGFtcGxlcyBvZiBOZXR3b3JrcyBhbmQgdGhlaXIgYXNzb2NpYXRlZCBlbnRpdGllczoKCiAgKiAqKlByb3RlaW4gLSBQcm90ZWluIGludGVyYWN0aW9uIG5ldHdvcmsqKiAtIGlzIGEgZGlyZWN0ZWQgb3IgdW5kaXJlY3RlZCBuZXR3b3JrIHdoZXJlIG5vZGVzIGluIHRoZSBuZXR3b3JrIGFyZSBwcm90ZWlucyBvciBnZW5lcyBhbmQgZWRnZXMgcmVwcmVzZW50IGhvdyB0aG9zZSBwcm90ZWlucyBpbnRlcmFjdC4gICAKICAqICoqR2VuZSAtIGdlbmUgaW50ZXJhY3Rpb24gbmV0d29yayoqIC0gbm9kZXMgaW4gdGhlIG5ldHdvcmsgYXJlIGdlbmVzIGFuZCBlZGdlcyBjYW4gcmVwcmVzZW50IHN5bnRoZXRpYyBsZXRoYWxpdHkgaS5lLiB0d28gZ2VuZXMgaGF2ZSBhIGNvbm5lY3Rpb24gaWYgZGVsZXRpbmcgYm90aCBvZiB0aGVtIGNhdXNlIGEgZGVjcmVhc2UgaW4gZml0bmVzcy4gIAogICogKipDb2V4cHJlc3Npb24gbmV0d29yayoqIC0gbm9kZXMgaW4gdGhlIG5ldHdvcmsgYXJlIGdlbmVzIG9yIHByb3RlaW5zIGFuZCBlZGdlcyByZXByZXNlbnQgdGhlIGRlZ3JlZSBvZiBjby1leHByZXNzaW9uIHRoZSB0d28gZ2VuZXMgaGF2ZS4gIE9mdGVuIHRoZSBlZGdlcyBhcmUgYXNzb2NpYXRlZCB3aXRoIGEgY29ycmVsYXRpb24gc2NvcmUgKGkuZS4gcGVhcnNvbiBjb3JyZWxhdGlvbikgYW5kIGVkZ2VzIGFyZSBmaWx0ZXJlZCBieSBhIGRlZmluZWQgdGhyZXNob2xkLiAgSWYgbm8gdGhyZXNob2xkIGlzIHNwZWNpZmllZCBhbGwgZ2VuZXMgd2lsbCBiZSBjb25uZWN0ZWQgdG8gYWxsIG90aGVyIGdlbmVzIGNyZWF0aW5nIGEgaGFpcmJhbGwuCiAgKiAqKkVucmljaG1lbnQgTWFwKiogLSBub2RlcyBpbiB0aGUgbmV0d29ya3MgYXJlIGdyb3VwcyBvZiBnZW5lcyBmcm9tIHBhdGh3YXlzIG9yIGZ1bmN0aW9ucyAoaS5lLiBnZW5lc2V0cykgYW5kIGVkZ2VzIHJlcHJlc2VudCBwYXRod2F5IGNyb3NzdGFsayAoIGdlbmVzIGluIGNvbW1vbikuCiAgKiAqKlNvY2lhbCBuZXR3b3JrKiogLSAgbm9kZXMgaW4gdGhlIG5ldHdvcmsgYXJlIGluZGl2aWR1YWxzIGFuZCBlZGdlcyBhcmUgc29tZSBzb3J0IG9mIHNvY2lhbCBpbnRlcmFjdGlvbiBiZXR3ZWVuIHR3byBpbmRpdmlkdWFscywgZm9yIGV4YW1wbGUsIGZyaWVuZHMgb24gRmFjZWJvb2ssIGxpbmtlZCBpbiBMaW5rZWRJTiwgLi4uCiAgKiAqKkNvcHVibGljYXRpb24gbmV0d29yayoqIC0gYSBzcGVjaWFsaXphdGlvbiBvZiB0aGUgc29jaWFsIG5ldHdvcmsgZm9yIHJlc2VhcmNoIHB1cnBvc2VzLiBOb2RlcyBpbiB0aGUgbmV0d29yayBhcmUgaW5kaXZpZHVhbHMgYW5kIGVkZ2VzIGJldHdlZW4gYW55IHR3byBpbmRpdmlkdWFscyBmb3IgdGhvc2Ugd2hvIGhhdmUgY28tYXV0aG9yZWQgYSBwdWJsaWNhdGlvbiB0b2dldGhlci4KICAKCiMjIE5ldHdvcmtzIGFzIFRvb2xzCk5ldHdvcmtzIGNhbiBiZSB1c2VkIGZvciB0d28gbWFpbiBwdXJwb3NlcyBidXQgb2Z0ZW4gZ28gaGFuZCBpbiBoYW5kLiAgCgoqKkFuYWx5c2lzKioKCiAqIFRvcG9sb2dpY2FsIHByb3BlcnRpZXMgLSBpbmNsdWRpbmcgbnVtYmVyIG9mIG5vZGVzLCBudW1iZXIgb2YgZWRnZXMsIG5vZGUgZGVncmVlLCBhdmVyYWdlIGNsdXN0ZXJpbmcgY29lZmZpY2llbnRzLCBzaG9ydGVzdCBwYXRoIGxlbmd0aHMsIGRlbnNpdHksIGFuZCBtYW55IG1vcmUuICBUb3BvbG9naWNhbCBwcm9wZXJ0aWVzIGNhbiBoZWxwIGdhaW4gaW5zaWdodHMgaW50byB0aGUgc3RydWN0dXJlIGFuZCBvcmdhbml6YXRpb24gb2YgdGhlIHJlc3VsdGluZyBiaW9sb2dpY2FsIG5ldHdvcmtzIGFzIHdlbGwgYXMgaGVscCBoaWdobGlnaHQgc3BlY2lmaWMgbm9kZSBvciByZWdpb25zIG9mIHRoZSBuZXR3b3JrLiAgCiAqIEh1YnMgYW5kIHN1Ym5ldHdvcmtzIC0gYSBodWIgaXMgZ2VuZXJhbGx5IGEgaGlnaGx5IGNvbm5lY3RlZCBub2RlIGluIGEgc2NhbGUtZnJlZSBuZXR3b3JrLiAgUmVtb3ZhbCBvZiBodWJzIGNhdXNlIHJhcGlkIGJyZWFrZG93biBvZiB0aGUgdW5kZXJseWluZyBuZXR3b3JrLiAgU3VibmV0d29ya3MgYXJlIGludGVyY29ubmVjdGVkIHJlZ2lvbnMgb2YgdGhlIG5ldHdvcmsgYW5kIGNhbiBiZSBkZWZpbmVkIGJ5IGFueSB1c2VyIGRlZmluZWQgcGFyYW1ldGVyLiAgCiAqIENsdXN0ZXIsIGNsYXNzaWZ5LCBhbmQgZGlmZnVzZSAgCiAqIERhdGEgaW50ZWdyYXRpb24KIAoqKlZpc3VhbGl6YXRpb24qKgoKICogRGF0YSBvdmVybGF5cwogKiBMYXlvdXRzIGFuZCBhbmltYXRpb24KICogRXhwbG9yYXRvcnkgYW5hbHlzaXMKICogQ29udGV4dCBhbmQgaW50ZXJwcmV0YXRpb24KIAojIFRyYW5zbGF0aW5nIGJpb2xvZ2ljYWwgZGF0YSBpbnRvIEN5dG9zY2FwZSB1c2luZyBSQ3kzCgpOZXR3b3JrcyBvZmZlciB1cyBhIHVzZWZ1bCB3YXkgdG8gcmVwcmVzZW50IG91ciBiaW9sb2dpY2FsIGRhdGEuICBCdXQgaG93IGRvIHdlIHNlYW1sZXNzbHkgdHJhbnNsYXRlIG91ciBkYXRhIGZyb20gUiBpbnRvIEN5dG9zY2FwZT8KCjxjZW50ZXI+CiFbXShodHRwczovL2N5dG9zY2FwZS5naXRodWIuaW8vY3l0b3NjYXBlLWF1dG9tYXRpb24vZm9yLXNjcmlwdGVycy9SL25vdGVib29rcy9iaW9jMjAxOF9SY3kzX2ludHJvLzIzMF9Jc3Nlcmxpbl9SQ3kzX2ludHJvL2ltYWdlcy9DeXRvc2NhcGVBdXRvbWF0aW9uXzMucG5nKQo8L2NlbnRlcj4KClRoZXJlIGFyZSBtdWx0aXBsZSB3YXlzIHRvIGNvbW11bmljYXRlIHdpdGggQ3l0b3NjYXBlIHByb2dyYW1tYXRpY2FsbHkuICBUaGVyZSBhcmUgdHdvIG1haW4gY29tcGxlbWVudGFyeSBwb3J0YWxzLCoqY3lSZXN0KipbQGN5cmVzdF0gYW5kICoqQ29tbWFuZHMqKiwgdGhhdCBmb3JtIHRoZSBmb3VuZGF0aW9uLiAgY3lSZXN0IHRyYW5zZm9ybXMgQ3l0b3NjYXBlIGluIHRvIGEgUkVTVCAoUmVwcmVzZW50YXRpb25hbCBTdGF0ZSBUcmFuc2ZlcikgZW5hYmxlZCBzZXJ2aWNlIHdoZXJlIGl0IGVzc2VudGlhbGx5IGxpc3RlbnMgZm9yIGV2ZW50cyB0aHJvdWdoIGEgcHJlZGVmaW5lZCBwb3J0IChieSBkZWZhdWx0IHBvcnQgMTIzNCkuICBUaGUgY3lSZXN0IGZ1bmN0aW9uYWxpdHkgc3RhcnRlZCBhcyBhbiBhcHAgYWRkIGluIGJ1dCBoYXMgbm93IGJlZW4gaW5jb3Jwb3JhdGVkIGludG8gdGhlIG1haW4gcmVsZWFzZS4gQ29tbWFuZHMsIG9uIHRoZSBvdGhlciBoYW5kLCBvZmZlciBhIG1lY2hhbmlzbSB3aGVyZWJ5IGFwcCBkZXZlbG9wZXJzIGNhbiBleHBvc2UgdGhlaXIgZnVuY3Rpb25hbGl0eSB0byBvdGhlciBhcHBzIG9yIHRvIHVzZXIgdGhyb3VnaCB0aGUgY29tbWFuZCBpbnRlcmZhY2UuICBQcmlvciB0byB0aGUgaW1wbGVtZW50YXRpb24gb2YgY3lSZXN0IG1hbnkgb2YgdGhlIGJhc2ljIG5ldHdvcmsgZnVuY3Rpb25zIHdlcmUgZmlyc3QgYXZhaWxhYmxlIGFzIGNvbW1hbmRzIHNvIHRoZXJlIGlzIHNvbWUgb3ZlcmxhcCBiZXR3ZWVuIHRoZSB0d28gZGlmZmVyZW50IG1ldGhvZHMuICBcQHJlZihmaWc6Y3l0b3NjYXBlUmN5Mykgc2hvd3MgdGhlIGRpZmZlcmVudCB3YXlzIHlvdSBjYW4gY2FsbCBDeXRvc2NhcGUuCgojIyBTZXQgdXAKYGBge3IgaW5jbHVkZT1GQUxTRX0KZXZhbCA9IEZBTFNFCmBgYAoKCkluIG9yZGVyIHRvIGNyZWF0ZSBuZXR3b3JrcyBpbiBDeXRvc2NhcGUgZnJvbSBSIHlvdSBuZWVkOgoKICogKipSQ3kzKiogLSBhIGJpb2NvbmR1dG9yIHBhY2thZ2UKIApgYGB7ciBpbnN0YWxsUmN5M30KaWYoISJSQ3kzIiAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpKXsKICAgIGluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikKICAgIEJpb2NNYW5hZ2VyOjppbnN0YWxsKCJSQ3kzIikKfQpsaWJyYXJ5KFJDeTMpCmBgYAoKICogKipDeXRvc2NhcGUqKiAtIERvd25sb2FkIGFuZCBpbnN0YWxsIFtDeXRvc2NhcGUgMy42LjEuXShjeXRvc2NhcGUub3JnL2Rvd25sb2FkLnBocCkgb3IgaGlnaGVyLiAgSmF2YSA5IGluIG5vdCBzdXBwb3J0ZWQuICBQbGVhc2UgbWFrZSBzdXJlIHRoYXQgSmF2YSA4IGlzIGluc3RhbGxlZC4KICAgKiBJbnN0YWxsIGFkZGl0aW9uYWwgY3l0b3NjYXBlIGFwcHMgdGhhdCB3aWxsIGJlIHVzZWQgaW4gdGhpcyB3b3Jrc2hvcC4gIElmIHVzaW5nIGN5dG9zY2FwZSAzLjYuMSBvciBvbGRlciB0aGUgYXBwcyBuZWVkIHRvIG1hbnVhbGx5IGluc3RhbGxlZCB0aHJvdWdoIHRoZSBbYXBwIG1hbmFnZXJdKGh0dHA6Ly9tYW51YWwuY3l0b3NjYXBlLm9yZy9lbi9sYXRlc3QvQXBwX01hbmFnZXIuaHRtbCNpbnN0YWxsaW5nLWFwcHMpIGluIEN5dG9zY2FwZSBvciB0aHJvdWdoIHlvdXIgW3dlYiBicm93c2VyXShodHRwczovL2FwcHMuY3l0b3NjYXBlLm9yZy9oZWxwL2dldHN0YXJ0ZWRfYXBwX2luc3RhbGwpLiAgKGNsaWNrIG9uIHRoZSBtZXRob2QgdG8gc2VlIGRldGFpbGVkIGluc3RydWN0aW9ucykKICAgICAqIFtGdW5jdGlvbmFsIEVucmljaG1lbnQgQ29sbGVjdGlvbl0oaHR0cDovL2FwcHMuY3l0b3NjYXBlLm9yZy9hcHBzL2Z1bmN0aW9uYWxlbnJpY2htZW50Y29sbGVjdGlvbikgLWEgY29sbGVjdGlvbiBvZiBhcHBzIHRvIHJldHJpZXZlIG5ldHdvcmtzIGFuZCBwYXRod2F5cywgaW50ZWdyYXRlIGFuZCBleHBsb3JlIHRoZSBkYXRhLCBwZXJmb3JtIGZ1bmN0aW9uYWwgZW5yaWNobWVudCBhbmFseXNpcywgYW5kIGludGVycHJldCBhbmQgZGlzcGxheSB5b3VyIHJlc3VsdHMuCiAgICAgKiBbRW5yaWNobWVudE1hcCBQaXBlbGluZSBDb2xsZWN0aW9uXShodHRwOi8vYXBwcy5jeXRvc2NhcGUub3JnL2FwcHMvZW5yaWNobWVudG1hcHBpcGVsaW5lY29sbGVjdGlvbikgLSBhIGNvbGxlY3Rpb24gb2YgYXBwcyBpbmNsdWRpbmcgRW5yaWNobWVudE1hcFtAZW5yaWNobWVudG1hcF0sIEF1dG9Bbm5vdGF0ZVtAYXV0b2Fubm90YXRlXSwgV29yZENsb3VkW0B3b3JkY2xvdWRdIGFuZCBjbHVzdGVyTWFrZXIyW0BjbHVzdGVybWFrZXJdIHVzZWQgdG8gdmlzdWFsaXplIGFuZCBhbmFseXNpcyBlbnJpY2htZW50IHJlc3VsdHMuCgpJZiB5b3UgYXJlIHVzaW5nIEN5dG9zY2FwZSAzLjcgb3IgaGlnaGVyIHRoZW4gYXBwcyBjYW4gYmUgaW5zdGFsbGVkIGRpcmVjdGx5IGZyb20gUi4KCmBgYHtyfQojYXZhaWxhYmxlIGluIEN5dG9zY2FwZSAzLjcuMCBhbmQgYWJvdmUKaW5zdGFsbEFwcCgnU1RSSU5HYXBwJykgIAppbnN0YWxsQXBwKCdhTWF0UmVhZGVyJykKaW5zdGFsbEFwcCgnY2x1c3Rlck1ha2VyMicpCmBgYAogICAKKipNYWtlIHN1cmUgdGhhdCBDeXRvc2NhcGUgaXMgcnVubmluZyoqCmBgYHtyIGV2YWw9RkFMU0V9CiBjeXRvc2NhcGVQaW5nICgpCmBgYAoKIyMgR2V0dGluZyBzdGFydGVkCiMjIyBDb25maXJtIHRoYXQgQ3l0b3NjYXBlIGlzIGluc3RhbGxlZCBhbmQgb3BlbmVkCmBgYHtyIGV2YWw9RkFMU0V9CiBjeXRvc2NhcGVWZXJzaW9uSW5mbyAoKQpgYGAKCiMjIyBCcm93c2UgYXZhaWxhYmxlIGZ1bmN0aW9ucywgY29tbWFuZHMgYW5kIGFyZ3VtZW50cwpEZXBlbmRpbmcgb24gd2hhdCBhcHBzIHlvdSBoYXZlIGluc3RhbGxlZCB0aGVyZSBpcyBkaWZmZXJlbnQgZnVuY3Rpb25hbGl0eSBhdmFpbGFibGUuICAKClRvIHNlZSBhbGwgdGhlIGZ1bmN0aW9ucyBhdmFpbGFibGUgaW4gUkN5MyBwYWNrYWdlCmBgYHtyfQpoZWxwKHBhY2thZ2U9UkN5MykKYGBgCgpPcGVuIHN3YWdnZXIgZG9jcyBmb3IgbGl2ZSBpbnN0YW5jZXMgb2YgQ3lSRVNUIEFQSS4gIFRoZSBDeVJFU1QgIEFQSSBsaXN0IGFsbCB0aGUgZnVuY3Rpb25zIGF2YWlsYWJsZSBpbiBhIGJhc2UgZGlzdHJpYnV0aW9uIG9mIGN5dG9zY2FwZS4gIFRoZSBiZWxvdyBjb21tYW5kIHdpbGwgbGF1bmNoIHRoZSBzd2FnZ2VyIGRvY3VtZW50YXRpb24gaW4gYSB3ZWIgYnJvd3Nlci4gIEZ1bmN0aW9ucyBhcmUgY2x1c3RlcmVkIGludG8gY2F0ZWdvcmllcy4gIEV4cGFuZGluZyBpbmRpdmlkdWFsIGNhdGVnb3JpZXMgd2lsbCBzaG93IGFsbCB0aGUgb3B0aW9uIGF2YWlsYWJsZS4gIEZ1cnRoZXIgZXhwYW5kaW5nIGFuIGluZGl2aWR1YWwgY29tbWFuZCB3aWxsIHNob3cgZGV0YWlsZWQgZG9jdW1lbnRhdGlvbiBmb3IgdGhlIGZ1bmN0aW9uLCBpbnB1dCwgb3V0cHV0cyBhbmQgYWxsb3cgeW91IHRvIHRyeSBhbmQgcnVuIHRoZSBmdW5jdGlvbi4gIFJ1bm5pbmcgdGhlIGZ1bmN0aW9uIHdpbGwgc2hvdyB0aGUgdXJsIHVzZWQgZm9yIHRoZSBxdWVyeSBhbmQgYWxsIHJldHVybmVkIHJlc3BvbnNlcy4gCmBgYHtyIGV2YWw9RkFMU0V9CmN5cmVzdEFQSSgpICAjIEN5UkVTVCBBUEkKYGBgCgpBcyBtZW50aW9uZWQgYWJvdmUsIHRoZXJlIGFyZSB0d28gd2F5cyB0byBpbnRlcmFjdCB3aXRoIEN5dG9zY2FwZSwgdGhyb3VnaCB0aGUgQ3lyZXN0IEFQSSBvciBjb21tYW5kcy4gIFRvIHNlZSB0aGUgYXZhaWxhYmxlIGNvbW1hbmRzIGluIHN3YWdnZXIgc2ltaWxhciB0byB0aGUgQ3lyZXN0IEFQSS4KYGBge3IgZXZhbD1GQUxTRX0KY29tbWFuZHNBUEkoKSAgIyBDb21tYW5kcyBBUEkKYGBgCgpUbyBnZXQgaW5mb3JtYXRpb24gYWJvdXQgYW4gaW5kaXZpZHVhbCBjb21tYW5kIGZyb20gdGhlIFIgZW52aXJvbm1lbnQgeW91IGNhbiBhbHNvIHVzZSB0aGUgY29tbWFuZHNIZWxwIGZ1bmN0aW9uLiAgU2ltcGx5IHNwZWNpZnkgd2hhdCBjb21tYW5kIHlvdSB3b3VsZCBsaWtlIHRvIGdldCBpbmZvcm1hdGlvbiBvbiBieSBhZGRpbmcgaXRzIG5hbWUgdG8gdGhlIGNvbW1hbmQuICBGb3IgZXhhbXBsZSAiY29tbWFuZHNIZWxwKCJoZWxwIHN0cmluZyIpIgpgYGB7ciBldmFsPUZBTFNFfQpjb21tYW5kc0hlbHAoImhlbHAiKQpgYGAKCjxjZW50ZXI+CiFbXShodHRwczovL2N5dG9zY2FwZS5naXRodWIuaW8vY3l0b3NjYXBlLWF1dG9tYXRpb24vZm9yLXNjcmlwdGVycy9SL25vdGVib29rcy9iaW9jMjAxOF9SY3kzX2ludHJvLzIzMF9Jc3Nlcmxpbl9SQ3kzX2ludHJvL2ltYWdlcy9zY3JlZW5zaG90X2F2YWlsYWJsZWNvbW1hbmRzLnBuZykKPC9jZW50ZXI+CgojIyBDeXRvc2NhcGUgQmFzaWNzCkNyZWF0ZSBhIEN5dG9zY2FwZSBuZXR3b3JrIGZyb20gc29tZSBiYXNpYyBSIG9iamVjdHMKYGBge3J9Cm5vZGVzIDwtIGRhdGEuZnJhbWUoaWQ9Yygibm9kZSAwIiwibm9kZSAxIiwibm9kZSAyIiwibm9kZSAzIiksCiAgICAgICAgICAgZ3JvdXA9YygiQSIsIkEiLCJCIiwiQiIpLCAjIGNhdGVnb3JpY2FsIHN0cmluZ3MKICAgICAgICAgICBzY29yZT1hcy5pbnRlZ2VyKGMoMjAsMTAsMTUsNSkpLCAjIGludGVnZXJzCiAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycz1GQUxTRSkKZWRnZXMgPC0gZGF0YS5mcmFtZShzb3VyY2U9Yygibm9kZSAwIiwibm9kZSAwIiwibm9kZSAwIiwibm9kZSAyIiksCiAgICAgICAgICAgdGFyZ2V0PWMoIm5vZGUgMSIsIm5vZGUgMiIsIm5vZGUgMyIsIm5vZGUgMyIpLAogICAgICAgICAgIGludGVyYWN0aW9uPWMoImluaGliaXRzIiwiaW50ZXJhY3RzIiwiYWN0aXZhdGVzIiwiaW50ZXJhY3RzIiksICAjIG9wdGlvbmFsCiAgICAgICAgICAgd2VpZ2h0PWMoNS4xLDMuMCw1LjIsOS45KSwgIyBudW1lcmljCiAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycz1GQUxTRSkKYGBgCgpgYGB7ciBldmFsPUZBTFNFfQpjcmVhdGVOZXR3b3JrRnJvbURhdGFGcmFtZXMobm9kZXMsZWRnZXMsIHRpdGxlPSJteSBmaXJzdCBuZXR3b3JrIiwgY29sbGVjdGlvbj0iRGF0YUZyYW1lIEV4YW1wbGUiKQpgYGAKClJlbWVtYmVyLiBBbGwgbmV0d29ya3Mgd2UgbWFrZSBhcmUgY3JlYXRlZCBpbiBDeXRvc2NhcGUgc28gZ2V0IGFuIGltYWdlIG9mIHRoZSByZXN1bHRpbmcgbmV0d29yayBhbmQgaW5jbHVkZSBpdCBpbiB5b3VyIGN1cnJlbnQgYW5hbHlzaXMgaWYgZGVzaXJlZC4gV2UgY2FuIGRlZmluZSBhIGZpbGVuYW1lIHRvIGV4cG9ydCB0bywgaW4gdGhlIGN1cnJlbnQgZGlyZWN0b3J5LgoKYGBge3J9CmluaXRpYWxfbmV0d29ya19wbmdfZmlsZV9uYW1lIDwtIGZpbGUucGF0aChnZXR3ZCgpLCJpbml0aWFsX2V4YW1wbGVfbmV0d29yay5wbmciKQpgYGAKCjxjZW50ZXI+CiFbXShodHRwczovL2N5dG9zY2FwZS5naXRodWIuaW8vY3l0b3NjYXBlLWF1dG9tYXRpb24vZm9yLXNjcmlwdGVycy9SL25vdGVib29rcy9iaW9jMjAxOF9SY3kzX2ludHJvLzIzMF9Jc3Nlcmxpbl9SQ3kzX2ludHJvL2ltYWdlcy9pbml0aWFsX2V4YW1wbGVfbmV0d29yay5wbmcpCjwvY2VudGVyPgoKYGBge3IgZXZhbD1GQUxTRX0KaWYoZmlsZS5leGlzdHMoaW5pdGlhbF9uZXR3b3JrX3BuZ19maWxlX25hbWUpKXsKICAjY3l0b3NjYXBlIGhhbmdzIHdhaXRpbmcgZm9yIHVzZXIgcmVzcG9uc2UgaWYgZmlsZSBhbHJlYWR5IGV4aXN0cy4gIFJlbW92ZSBpdCBmaXJzdAogIGZpbGUucmVtb3ZlKGluaXRpYWxfbmV0d29ya19wbmdfZmlsZV9uYW1lKQogIH0gCgojZXhwb3J0IHRoZSBuZXR3b3JrCmV4cG9ydEltYWdlKGZpbGVuYW1lPSJpbml0aWFsX2V4YW1wbGVfbmV0d29yay5wbmciLCB0eXBlID0gInBuZyIpCmBgYAoKIyMgRXhhbXBsZSBEYXRhIFNldApXZSBkb3dubG9hZGVkIGdlbmUgZXhwcmVzc2lvbiBkYXRhIGZyb20gdGhlIE92YXJpYW4gU2Vyb3VzIEN5c3RhZGVub2NhcmNpbm9tYSBwcm9qZWN0IG9mIFRoZSBDYW5jZXIgR2Vub21lIEF0bGFzIChUQ0dBKVtAVENHQV0sIGh0dHA6Ly9jYW5jZXJnZW5vbWUubmloLmdvdiB2aWEgdGhlIEdlbm9taWMgRGF0YSBDb21tb25zIChHREMpIHBvcnRhbFtAR0RDXSBvbiAyMDE3LTA2LTE0IHVzaW5nIFRDR0FCaW9saW5rcyBSIHBhY2thZ2VbQFRDR0FCaW9saW5rc10uIFRoZSBkYXRhIGluY2x1ZGVzIDMwMCBzYW1wbGVzIGF2YWlsYWJsZSBhcyBSTkEtc2VxIGRhdGEsIHdpdGggcmVhZHMgbWFwcGVkIHRvIGEgcmVmZXJlbmNlIGdlbm9tZSB1c2luZyBNYXBTcGxpY2VbQE1hcFNwbGljZV0gYW5kIHJlYWQgY291bnRzIHBlciB0cmFuc2NyaXB0IGRldGVybWluZWQgdXNpbmcgdGhlIFJTRU0gbWV0aG9kW0BSU0VNXS4gUk5BLXNlcSBkYXRhIGFyZSBsYWJlbGVkIGFzIOKAmFJOQS1TZXEgVjLigJksIHNlZSBkZXRhaWxzIGF0OiBodHRwczovL3dpa2kubmNpLm5paC5nb3YvZGlzcGxheS9UQ0dBL1JOQVNlcStWZXJzaW9uKzIpLiBUaGUgUk5BLVNlcVYyIGRhdGEgY29uc2lzdHMgb2YgcmF3IGNvdW50cyBzaW1pbGFyIHRvIHJlZ3VsYXIgUk5BLXNlcSBidXQgUlNFTSAoUk5BLVNlcSBieSBFeHBlY3RhdGlvbiBNYXhpbWl6YXRpb24pIGRhdGEgY2FuIGJlIHVzZWQgd2l0aCB0aGUgZWRnZVIgbWV0aG9kLiBUaGUgZXhwcmVzc2lvbiBkYXRhc2V0IG9mIDMwMCB0dW1vdXJzLCB3aXRoIDc5IGNsYXNzaWZpZWQgYXMgSW1tdW5vcmVhY3RpdmUsIDcyIGNsYXNzaWZpZWQgYXMgTWVzZW5jaHltYWwsIDY5IGNsYXNzaWZpZWQgYXMgRGlmZmVyZW50aWF0ZWQsIGFuZCA4MCBjbGFzc2lmaWVkIGFzIFByb2xpZmVyYXRpdmUgc2FtcGxlcyhjbGFzcyBkZWZpbml0aW9ucyB3ZXJlIG9idGFpbmVkIGZyb20gVmVyaGFhayBldCBhbC5bQE9WXSBTdXBwbGVtZW50YXJ5IFRhYmxlIDEsIHRoaXJkIGNvbHVtbikuIFJOQS1zZXEgcmVhZCBjb3VudHMgd2VyZSBjb252ZXJ0ZWQgdG8gQ1BNIHZhbHVlcyBhbmQgZ2VuZXMgd2l0aCBDUE0gPiAxIGluIGF0IGxlYXN0IDUwIG9mIHRoZSBzYW1wbGVzIGFyZSByZXRhaW5lZCBmb3IgZnVydGhlciBzdHVkeSAoNTAgaXMgdGhlIG1pbmltYWwgc2FtcGxlIHNpemUgaW4gdGhlIGNsYXNzZXMpLiAgVGhlIGRhdGEgd2FzIG5vcm1hbGl6ZWQgYW5kIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIHdhcyBjYWxjdWxhdGVkIGZvciBlYWNoIGNhbmNlciBjbGFzcyByZWxhdGl2ZSB0byB0aGUgcmVzdCBvZiB0aGUgc2FtcGxlcy4gCgpUaGVyZSBhcmUgdHdvIGRhdGEgZmlsZXM6CiAxLiBFeHByZXNzaW9uIG1hdHJpeCAtIGNvbnRhaW5pbmcgdGhlIG5vcm1hbGl6ZWQgZXhwcmVzc2lvbiBmb3IgZWFjaCBnZW5lIGFjcm9zcyBhbGwgMzAwIHNhbXBsZXMuCiAxLiBHZW5lIHJhbmtzIC0gY29udGFpbmluZyB0aGUgcC12YWx1ZXMsIEZEUiBhbmQgZm9sZGNoYW5nZSB2YWx1ZXMgZm9yIHRoZSA0IGNvbXBhcmlzb25zIChtZXNlbmNoeW1hbCB2cyByZXN0LCBkaWZmZXJlbnRpYWwgdnMgcmVzdCwgcHJvbGlmZXJhdGl2ZSB2cyByZXN0IGFuZCBpbW11bm9yZWFjdGl2ZSB2cyByZXN0KQoKVGhlIGZvbGxvd2luZyBjb21tYW5kcyB3aWxsIGNyZWF0ZSBhIG5ldyBkaXJlY3RvcnkgaW4geW91ciBjdXJyZW50IGRpcmVjdG9yeSwgYW5kIGRvd25sb2FkIHRoZSBkYXRhIGZpbGVzIHRvIGl0LgpgYGB7cn0KI2NyZWF0ZSBhIHNvdXJjZSBkaXJlY3RvcnkgZm9yIHRoZSBkb3dubG9hZApkaXIuY3JlYXRlKGZpbGUucGF0aChnZXR3ZCgpLCIyMzBfSXNzZXJsaW5fUkN5M19pbnRybyIpKQoKI2RlZmluZSBsb2NhdGlvbnMgZm9yIGRvd25sb2FkCnVybF9leHAgPC0gImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9jeXRvc2NhcGUvY3l0b3NjYXBlLXR1dG9yaWFscy9naC1wYWdlcy9wcmVzZW50YXRpb25zL21vZHVsZXMvUkN5M19FeGFtcGxlRGF0YS9kYXRhL1RDR0FfT1ZfUk5Bc2VxX2V4cHJlc3Npb24udHh0IgpwYXRoX2V4cCA8LSBmaWxlLnBhdGgoZ2V0d2QoKSwiMjMwX0lzc2VybGluX1JDeTNfaW50cm8iLCAiVENHQV9PVl9STkFzZXFfZXhwcmVzc2lvbi50eHQiKQoKdXJsX3Njb3JlcyA8LSJodHRwczovL2dpdGh1Yi5jb20vY3l0b3NjYXBlL2N5dG9zY2FwZS10dXRvcmlhbHMvYmxvYi9naC1wYWdlcy9wcmVzZW50YXRpb25zL21vZHVsZXMvUkN5M19FeGFtcGxlRGF0YS9kYXRhL1RDR0FfT1ZfUk5Bc2VxX0FsbF9lZGdlUl9zY29yZXMudHh0P3Jhdz10cnVlIgpwYXRoX3Njb3JlcyA8LSBmaWxlLnBhdGgoZ2V0d2QoKSwiMjMwX0lzc2VybGluX1JDeTNfaW50cm8iLCAiVENHQV9PVl9STkFzZXFfQWxsX2VkZ2VSX3Njb3Jlcy50eHQiKQoKI2Rvd25sb2FkIGZpbGVzCmRvd25sb2FkLmZpbGUodXJsX2V4cCwgcGF0aF9leHApCmRvd25sb2FkLmZpbGUodXJsX3Njb3JlcywgcGF0aF9zY29yZXMpCmBgYAoKTm93IHdlIGNhbiByZWFkIHRoZSBkb3dubG9hZGVkIGZpbGVzIGludG8gUjoKYGBge3J9ClJOQVNlcV9leHByZXNzaW9uX21hdHJpeCA8LSByZWFkLnRhYmxlKHBhdGhfZXhwLCBoZWFkZXIgPSBUUlVFLCBzZXAgPSAiXHQiLCBxdW90ZT0iXCIiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCgpSTkFTZXFfZ2VuZV9zY29yZXMgPC0gcmVhZC50YWJsZShwYXRoX3Njb3JlcywgaGVhZGVyID0gVFJVRSwgc2VwID0gIlx0IiwgcXVvdGU9IlwiIiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQpgYGAKCiMjIEZpbmRpbmcgTmV0d29yayBEYXRhCkhvdyBkbyBJIHJlcHJlc2VudCAqbXkqIGRhdGEgYXMgYSBuZXR3b3JrPwoKVW5mb3J0dW5hdGVseSwgdGhlcmUgaXMgbm90IGEgc2ltcGxlIGFuc3dlci4gICoqSXQgZGVwZW5kcyBvbiB5b3VyIGJpb2xvZ2ljYWwgcXVlc3Rpb24hKiogICAKCkV4YW1wbGUgdXNlIGNhc2VzOgoKIDEuIE9taWNzIGRhdGEgLSBJIGhhdmUgYSAqZmlsbCBpbiB0aGUgYmxhbmsqIChtaWNyb2FycmF5LCBSTkFTZXEsIFByb3Rlb21pY3MsIEFUQUNzZXEsIE1pY3JvUk5BLCBHV0FTIC4uLikgZGF0YXNldC4gIEkgaGF2ZSBub3JtYWxpemVkIGFuZCBzY29yZWQgbXkgZGF0YS4gSG93IGRvIEkgb3ZlcmxheSBteSBkYXRhIG9uIGV4aXN0aW5nIGludGVyYWN0aW9uIGRhdGE/IAogMS4gQ29leHByZXNzaW9uIGRhdGEgLSBJIGhhdmUgYSBkYXRhc2V0IHRoYXQgcmVwcmVzZW50cyByZWxhdGlvbnNoaXBzLiAgSG93IGRvIEkgcmVwcmVzZW50IGl0IGFzIGEgbmV0d29yay4gCgojIFVzZSBDYXNlIDEgLSBIb3cgYXJlIG15IHRvcCBnZW5lcyByZWxhdGVkPwoKT21pY3MgZGF0YSAtIEkgaGF2ZSBhICpmaWxsIGluIHRoZSBibGFuayogKG1pY3JvYXJyYXksIFJOQVNlcSwgUHJvdGVvbWljcywgQVRBQ3NlcSwgTWljcm9STkEsIEdXQVMgLi4uKSBkYXRhc2V0LiAgSSBoYXZlIG5vcm1hbGl6ZWQgYW5kIHNjb3JlZCBteSBkYXRhLiBIb3cgZG8gSSBvdmVybGF5IG15IGRhdGEgb24gZXhpc3RpbmcgaW50ZXJhY3Rpb24gZGF0YT8gCgpUaGVyZSBhcmUgZW5kbGVzcyBhbW91bnRzIG9mIGRhdGFiYXNlcyBzdG9yaW5nIGludGVyYWN0aW9uIGRhdGEuIAoKPGNlbnRlcj4KIVtdKGh0dHBzOi8vY3l0b3NjYXBlLmdpdGh1Yi5pby9jeXRvc2NhcGUtYXV0b21hdGlvbi9mb3Itc2NyaXB0ZXJzL1Ivbm90ZWJvb2tzL2Jpb2MyMDE4X1JjeTNfaW50cm8vMjMwX0lzc2VybGluX1JDeTNfaW50cm8vaW1hZ2VzL2ludGVyYWN0aW9uLWRicy5wbmcpCjwvY2VudGVyPgoKVGhhbmtmdWxseSB3ZSBkb24ndCBoYXZlIHRvIHF1ZXJ5IGVhY2ggaW5kZXBlbmRlbnQgYWxseS4gIEluIGFkZGl0aW9uIHRvIG1hbnkgc3BlY2lhbGl6ZWQgKGZvciBleGFtcGxlLCBmb3Igc3BlY2lmaWMgbW9sZWN1bGVzLCBpbnRlcmFjdGlvbiB0eXBlLCBvciBzcGVjaWVzKSBpbnRlcmFjdGlvbiBkYXRhYmFzZXMgdGhlcmUgYXJlIGFsc28gZGF0YWJhc2VzIHRoYXQgY29sbGF0ZSB0aGVzZSBkYXRhYmFzZXMgdG8gY3JlYXRlIGEgYnJvYWQgcmVzb3VyY2UgdGhhdCBpcyBlYXNpZXIgdG8gdXNlLiBGb3IgZXhhbXBsZToKCiAqIFtTdHJpbmdBcHBdKGh0dHA6Ly9hcHBzLmN5dG9zY2FwZS5vcmcvYXBwcy9zdHJpbmdhcHApIC0gaXMgYSBwcm90ZWluIC0gcHJvdGVpbiBhbmQgcHJvdGVpbi0gY2hlbWljYWwgZGF0YWJhc2UgdGhhdCBpbXBvcnRzIGRhdGEgZnJvbSBbU3RyaW5nXShzdHJpbmctZGIub3JnKVtAc3RyaW5nXSAod2hpY2ggaXRzZWxmIGluY2x1ZGVzIGRhdGEgZnJvbSBtdWx0aXBsZSBzcGVjaWVzLCBjb2V4cHJlc3Npb24sIHRleHQtbWluaW5nLGV4aXN0aW5nIGRhdGFiYXNlcywgYW5kIGdlbm9taWMgY29udGV4dCksIFtTVElUQ0hdIGludG8gYSB1bmlmaWVkLCBxdWVyaWFibGUgZGF0YWJhc2UuCiAqIFtQU0lDUVVJQ10oaHR0cHM6Ly9wc2ljcXVpYy5naXRodWIuaW8vKVtAcHNpY3F1aWNdIC0gYSBSRVNULWZ1bCBzZXJ2aWNlIHRoYXQgaXMgdGhlIHJlc3BvbnNpYmlsaXR5IG9mIHRoZSBkYXRhYmFzZSBwcm92aWRlciB0byBzZXQgdXAgYW5kIG1haW50YWluLiAgUFNJQ1FVSUMgaXMgYW4gYWRkaXRpb25hbCBpbnRlcmZhY2UgdGhhdCBhbGxvd3MgdXNlcnMgdG8gc2VhcmNoIGFsbCBhdmFpbGFibGUgZGF0YWJhc2VzICh0aGF0IHN1cHBvcnQgdGhpcyBSRVNULWZ1bCBzZXJ2aWNlKS4gIFRoZSBkYXRhYmFzZXMgYXJlIHJlcXVpcmVkIHRvIHJlcHJlc2VudCB0aGVpciBpbnRlcmFjdGlvbiBkYXRhIGluIFByb3Rlb21pYyBTdGFuZGFyZHMgSW5pdGlhdGl2ZSAtIG1vbGVjdWxhciBpbnRlcmFjdGlvbiAoUFNJLU1JKSBmb3JtYXQuIFRvIHNlZSBhIGxpc3Qgb2YgYWxsIHRoZSBhdmFpbGFibGUgZGF0YSBzb3VyY2Ugc2VlIFtoZXJlXShodHRwOi8vd3d3LmViaS5hYy51ay9Ub29scy93ZWJzZXJ2aWNlcy9wc2ljcXVpYy9yZWdpc3RyeS9yZWdpc3RyeT9hY3Rpb249U1RBVFVTKQogKiBbbkRleF0oaHR0cDovL2hvbWUubmRleGJpby5vcmcvaW5kZXgvKVtAbmRleF0gLSBhIG5ldHdvcmsgZGF0YSBleGNoYW5nZSByZXBvc2l0b3J5LiAKICogW0dlbmVNQU5JQV0oaHR0cDovL2dlbmVtYW5pYS5vcmcvKVtAZ2VuZW1hbmlhXSAtIGNvbnRhaW5zIG11bHRpcGxlIG5ldHdvcmtzIChzaGFyZWQgZG9tYWlucywgcGh5c2ljYWwgaW50ZXJhY3Rpb25zLCBwYXRod2F5cywgcHJlZGljdGVkLCBjby1leHByZXNzaW9uLCBnZW5ldGljIGludGVyYWN0aW9ucyBhbmQgY28tbG9jYWxpemVkIG5ldHdvcmspLiAgR2l2ZW4gYSBzZXQgb2YgZ2VuZXMgR2VuZU1BTklBIHNlbGVjdHMgYW5kIHdlaWdodHMgbmV0d29ya3MgdGhhdCBvcHRpbWl6ZSB0aGUgY29ubmVjdGl2aXR5IGJldHdlZW4gdGhlIHF1ZXJ5IGdlbmVzLiAgR2VuZU1BTklBIHdpbGwgYWxzbyByZXR1cm4gYWRkaXRpb25hbCBnZW5lcyB0aGF0IGFyZSBoaWdobHkgcmVsYXRlZCB0byB5b3VyIHF1ZXJ5IHNldC4KICogW1BhdGh3YXlDb21tb25zXShodHRwOi8vd3d3LnBhdGh3YXljb21tb25zLm9yZy8pIC0gKGFjY2VzcyB0aGUgZGF0YSB0aHJvdWdoIHRoZSBbQ3lQYXRoMkFwcF0oaHR0cDovL2FwcHMuY3l0b3NjYXBlLm9yZy9hcHBzL2N5cGF0aDIpKSBpcyBhIHBhdGh3YXkgYW5kIGludGVyYWN0aW9uIGRhdGEgc291cmNlLiAgRGF0YSBpcyBjb2xsYXRlZCBmcm9tIGEgbGFyZ2Ugc2V0IG9mIHJlc291cmNlcyAobGlzdCBbaGVyZV0oaHR0cDovL3d3dy5wYXRod2F5Y29tbW9ucy5vcmcvcGMyL2RhdGFzb3VyY2VzKSApIGFuZCBzdG9yZWQgaW4gdGhlIEJpb1BBWFtAYmlvcGF4XSBmb3JtYXQuICBCaW9QQVggaXMgYSBkYXRhIHN0YW5kYXJkIHRoYXQgYWxsb3dzIGZvciBkZXRhaWxlZCByZXByZXNlbnRhdGlvbiBvZiBwYXRod2F5IG1lY2hhbmlzdGljIGRldGFpbHMgYXMgb3Bwb3NlZCB0byBjb2xsYXBzaW5nIGl0IHRvIHRoZSBzZXQgb2YgaW50ZXJhY3Rpb25zIGJldHdlZW4gbW9sZWN1bGVzLiAgQmlvUEFYIHBhdGh3YXlzIGZyb20gUGF0aHdheSBjb21tb25zIGNhbiBhbHNvIGJlIGxvYWRlZCBkaXJlY3RseSBpbnRvIFIgdXNpbmcgdGhlIFtQYXhUb29sc1JdKGh0dHA6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL3BheHRvb2xzci5odG1sKVtAcGF4dG9vbHNyXSBCaW9jb25kdWN0b3IgcGFja2FnZS4gICAKCkdldCBhIHN1YnNldCBvZiBnZW5lcyBvZiBpbnRlcmVzdCBmcm9tIG91ciBzY29yZWQgZGF0YToKYGBge3J9CnRvcF9tZXNlbmNoeW1hbF9nZW5lcyA8LSBSTkFTZXFfZ2VuZV9zY29yZXNbd2hpY2goUk5BU2VxX2dlbmVfc2NvcmVzJEZEUi5tZXNlbiA8IDAuMDUgJiBSTkFTZXFfZ2VuZV9zY29yZXMkbG9nRkMubWVzZW4gPiAyKSxdCmhlYWQodG9wX21lc2VuY2h5bWFsX2dlbmVzKQpgYGAKCldlIGFyZSBnb2luZyB0byBxdWVyeSB0aGUgU3RyaW5nIERhdGFiYXNlIHRvIGdldCBhbGwgaW50ZXJhY3Rpb25zIGZvdW5kIGZvciBvdXIgc2V0IG9mIHRvcCBNZXNlbmNoeW1hbCBnZW5lcy4KClJlbWluZGVyOiB0byBzZWUgdGhlIHBhcmFtZXRlcnMgcmVxdWlyZWQgYnkgdGhlIHN0cmluZyBmdW5jdGlvbiBvciB0byBmaW5kIHRoZSByaWdodCBzdHJpbmcgZnVuY3Rpb24geW91IGNhbiB1c2UgY29tbWFuZHNIZWxwLgpgYGB7ciBldmFsPUZBTFNFfQpjb21tYW5kc0hlbHAoImhlbHAgc3RyaW5nIikKYGBgCgpgYGB7ciBldmFsPUZBTFNFfQpjb21tYW5kc0hlbHAoImhlbHAgc3RyaW5nIHByb3RlaW4gcXVlcnkiKQpgYGAKCmBgYHtyIGV2YWw9RkFMU0V9Cm1lc2VuX3N0cmluZ19pbnRlcmFjdGlvbl9jbWQgPC0gcGFzdGUoJ3N0cmluZyBwcm90ZWluIHF1ZXJ5IHRheG9uSUQ9OTYwNiBsaW1pdD0xNTAgY3V0b2ZmPTAuOSBxdWVyeT0iJyxwYXN0ZSh0b3BfbWVzZW5jaHltYWxfZ2VuZXMkTmFtZSwgY29sbGFwc2U9IiwiKSwnIicsc2VwPSIiKQpjb21tYW5kc0dFVChtZXNlbl9zdHJpbmdfaW50ZXJhY3Rpb25fY21kKQpgYGAKCkdldCBhIHNjcmVlbnNob3Qgb2YgdGhlIGluaXRpYWwgbmV0d29yawpgYGB7ciBpbml0aWFsX3N0cmluZ19uZXR3b3JrX3NjcmVlbnNob3QsIGluY2x1ZGU9VFJVRX0KaW5pdGlhbF9zdHJpbmdfbmV0d29ya19wbmdfZmlsZV9uYW1lIDwtIGZpbGUucGF0aChnZXR3ZCgpLCIyMzBfSXNzZXJsaW5fUkN5M19pbnRybyIsICJpbml0aWFsX3N0cmluZ19uZXR3b3JrLnBuZyIpCmBgYAoKYGBge3IgZXZhbD1GQUxTRX0KaWYoZmlsZS5leGlzdHMoaW5pdGlhbF9zdHJpbmdfbmV0d29ya19wbmdfZmlsZV9uYW1lKSl7CiAgI2N5dG9zY2FwZSBoYW5ncyB3YWl0aW5nIGZvciB1c2VyIHJlc3BvbnNlIGlmIGZpbGUgYWxyZWFkeSBleGlzdHMuICBSZW1vdmUgaXQgZmlyc3QKICByZXNwb25zZSA8LSBmaWxlLnJlbW92ZShpbml0aWFsX3N0cmluZ19uZXR3b3JrX3BuZ19maWxlX25hbWUpCn0gCgpyZXNwb25zZSA8LSBleHBvcnRJbWFnZShpbml0aWFsX3N0cmluZ19uZXR3b3JrX3BuZ19maWxlX25hbWUsIHR5cGUgPSAicG5nIikKYGBgCgpgYGB7ciBpbml0aWFsc3RyaW5nbmV0d29yaywgZWNobz1GQUxTRSwgZmlnLmNhcD0iSW5pdGlhbCBuZXR3b3JrIHJldHVybmVkIGJ5IFN0cmluZyBmcm9tIG91ciBzZXQgb2YgTWVzZW5jaHltYWwgcXVlcnkgZ2VuZXMifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhpbml0aWFsX3N0cmluZ19uZXR3b3JrX3BuZ19maWxlX25hbWUpCmBgYAoKTGF5b3V0IHRoZSBuZXR3b3JrCmBgYHtyIGV2YWw9RkFMU0V9CmxheW91dE5ldHdvcmsoJ2ZvcmNlLWRpcmVjdGVkJykKYGBgCgpDaGVjayB3aGF0IG90aGVyIGxheW91dCBhbGdvcml0aG1zIGFyZSBhdmFpbGFibGUgdG8gdHJ5IG91dApgYGB7ciBldmFsPUZBTFNFfQpnZXRMYXlvdXROYW1lcygpCmBgYAoKR2V0IHRoZSBwYXJhbWV0ZXJzIGZvciBhIHNwZWNpZmljIGxheW91dApgYGB7ciBldmFsPUZBTFNFfQpnZXRMYXlvdXRQcm9wZXJ0eU5hbWVzKGxheW91dC5uYW1lPSdmb3JjZS1kaXJlY3RlZCcpCmBgYAoKUmUtbGF5b3V0IHRoZSBuZXR3b3JrIHVzaW5nIHRoZSBmb3JjZSBkaXJlY3RlZCBsYXlvdXQgYnV0IHNwZWNpZnkgc29tZSBvZiB0aGUgcGFyYW1ldGVycyAKYGBge3IgZXZhbD1GQUxTRX0KbGF5b3V0TmV0d29yaygnZm9yY2UtZGlyZWN0ZWQgZGVmYXVsdFNwcmluZ0NvZWZmaWNpZW50PTAuMDAwMDAwOCBkZWZhdWx0U3ByaW5nTGVuZ3RoPTcwJykKYGBgCgpHZXQgYSBzY3JlZW5zaG90IG9mIHRoZSByZS1sYWlkIG91dCBuZXR3b3JrCmBgYHtyIHJlbGF5b3V0X3N0cmluZ19uZXR3b3JrX3NjcmVlbnNob3QsIGluY2x1ZGU9VFJVRX0KcmVsYXlvdXRfc3RyaW5nX25ldHdvcmtfcG5nX2ZpbGVfbmFtZSA8LSBmaWxlLnBhdGgoZ2V0d2QoKSwiMjMwX0lzc2VybGluX1JDeTNfaW50cm8iLCAicmVsYXlvdXRfc3RyaW5nX25ldHdvcmsucG5nIikKYGBgCgpgYGB7ciBldmFsPUZBTFNFfQppZihmaWxlLmV4aXN0cyhyZWxheW91dF9zdHJpbmdfbmV0d29ya19wbmdfZmlsZV9uYW1lKSl7CiAgI2N5dG9zY2FwZSBoYW5ncyB3YWl0aW5nIGZvciB1c2VyIHJlc3BvbnNlIGlmIGZpbGUgYWxyZWFkeSBleGlzdHMuICBSZW1vdmUgaXQgZmlyc3QKICByZXNwb25zZTwtIGZpbGUucmVtb3ZlKHJlbGF5b3V0X3N0cmluZ19uZXR3b3JrX3BuZ19maWxlX25hbWUpCiAgfSAKcmVzcG9uc2UgPC0gZXhwb3J0SW1hZ2UocmVsYXlvdXRfc3RyaW5nX25ldHdvcmtfcG5nX2ZpbGVfbmFtZSwgdHlwZSA9ICJwbmciKQpgYGAKCmBgYHtyIHJlbGF5b3V0c3RyaW5nbmV0d29yaywgZWNobz1GQUxTRSwgZmlnLmNhcD0iSW5pdGlhbCBuZXR3b3JrIHJldHVybmVkIGJ5IFN0cmluZyBmcm9tIG91ciBzZXQgb2YgTWVzZW5jaHltYWwgcXVlcnkgZ2VuZXMifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhyZWxheW91dF9zdHJpbmdfbmV0d29ya19wbmdfZmlsZV9uYW1lKQpgYGAKCk92ZXJsYXkgb3VyIGV4cHJlc3Npb24gZGF0YSBvbiB0aGUgU3RyaW5nIG5ldHdvcmsuICAKVG8gZG8gdGhpcyB3ZSB3aWxsIGJlIHVzaW5nIHRoZSBsb2FkVGFibGVEYXRhIGZ1bmN0aW9uIGZyb20gUkN5My4gSXQgaXMgaW1wb3J0YW50IHRvIG1ha2Ugc3VyZSB0aGF0ICB0aGF0IHlvdXIgaWRlbnRpZmllcnMgdHlwZXMgbWF0Y2ggdXAuICBZb3UgY2FuIGNoZWNrIHdoYXQgaXMgdXNlZCBieSBTdHJpbmcgYnkgcHVsbGluZyBpbiB0aGUgY29sdW1uIG5hbWVzIG9mIHRoZSBub2RlIGF0dHJpYnV0ZSB0YWJsZS4KCmBgYHtyIGV2YWw9RkFMU0V9CmdldFRhYmxlQ29sdW1uTmFtZXMoJ25vZGUnKQpgYGAKCklmIHlvdSBhcmUgdW5zdXJlIG9mIHdoYXQgZWFjaCBjb2x1bW4gaXMgYW5kIHdhbnQgdG8gZnVydGhlciB2ZXJpZnkgdGhlIGNvbHVtbiB0byB1c2UgeW91IGNhbiBhbHNvIHB1bGwgaW4gdGhlIGVudGlyZSBub2RlIGF0dHJpYnV0ZSB0YWJsZS4KYGBge3IgZXZhbD1GQUxTRX0Kbm9kZV9hdHRyaWJ1dGVfdGFibGVfdG9wbWVzZW4gPC0gZ2V0VGFibGVDb2x1bW5zKHRhYmxlPSJub2RlIikKaGVhZChub2RlX2F0dHJpYnV0ZV90YWJsZV90b3BtZXNlblssMzo3XSkKYGBgCgpUaGUgY29sdW1uICJkaXNwbGF5IG5hbWUiIGNvbnRhaW5zIEhHTkMgZ2VuZSBuYW1lcyB3aGljaCBhcmUgYWxzbyBmb3VuZCBpbiBvdXIgT3ZhcmlhbiBDYW5jZXIgZGF0YXNldC4KClRvIGltcG9ydCBvdXIgZXhwcmVzc2lvbiBkYXRhIHdlIHdpbGwgbWF0Y2ggb3VyIGRhdGFzZXQgdG8gdGhlICJkaXNwbGF5IG5hbWUiIG5vZGUgYXR0cmlidXRlLgoKYGBge3IgZXZhbD1GQUxTRX0KP2xvYWRUYWJsZURhdGEKCmxvYWRUYWJsZURhdGEoUk5BU2VxX2dlbmVfc2NvcmVzLHRhYmxlLmtleS5jb2x1bW4gPSAiZGlzcGxheSBuYW1lIixkYXRhLmtleS5jb2x1bW4gPSAiTmFtZSIpICAjZGVmYXVsdCBkYXRhLmZyYW1lIGtleSBpcyByb3cubmFtZXMKYGBgCgpNb2RpZnkgdGhlIFZpc3VhbCBTdHlsZQpDcmVhdGUgeW91ciBvd24gdmlzdWFsIHN0eWxlIHRvIHZpc3VhbGl6ZSB5b3VyIGV4cHJlc3Npb24gZGF0YSBvbiB0aGUgU3RyaW5nIG5ldHdvcmsuIAoKU3RhcnQgd2l0aCBhIGRlZmF1bHQgc3R5bGUKYGBge3IgZXZhbD1GQUxTRX0Kc3R5bGUubmFtZSA9ICJNZXNlbmNoeW1hbFN0eWxlIgpkZWZhdWx0cy5saXN0IDwtIGxpc3QoTk9ERV9TSEFQRT0iZWxsaXBzZSIsCiAgICAgICAgICAgICAgICAgTk9ERV9TSVpFPTYwLAogICAgICAgICAgICAgICAgIE5PREVfRklMTF9DT0xPUj0iI0FBQUFBQSIsCiAgICAgICAgICAgICAgICAgRURHRV9UUkFOU1BBUkVOQ1k9MTIwKQpub2RlLmxhYmVsLm1hcCA8LSBtYXBWaXN1YWxQcm9wZXJ0eSgnbm9kZSBsYWJlbCcsJ2Rpc3BsYXkgbmFtZScsJ3AnKSAjIHAgZm9yIHBhc3N0aHJvdWdoOyBub3RoaW5nIGVsc2UgbmVlZGVkCmNyZWF0ZVZpc3VhbFN0eWxlKHN0eWxlLm5hbWUsIGRlZmF1bHRzLmxpc3QsIGxpc3Qobm9kZS5sYWJlbC5tYXApKQpzZXRWaXN1YWxTdHlsZShzdHlsZS5uYW1lPXN0eWxlLm5hbWUpCmBgYAoKVXBkYXRlIHlvdXIgY3JlYXRlZCBzdHlsZSB3aXRoIGEgbWFwcGluZyBmb3IgdGhlIE1lc2VuY2h5bWFsIGxvZ0ZDIGV4cHJlc3Npb24uIFRoZSBmaXJzdCBzdGVwIGlzIHRvIGdyYWIgdGhlIGNvbHVtbiBkYXRhIGZyb20gQ3l0b3NjYXBlICh3ZSBjYW4gcmV1c2UgdGhlIG5vZGVfYXR0cmlidXRlIHRhYmxlIGNvbmNlcHQgZnJvbSBhYm92ZSBidXQgd2UgaGF2ZSB0byBjYWxsIHRoZSBmdW5jdGlvbiBhZ2FpbiBhcyB3ZSBoYXZlIHNpbmNlIGFkZGVkIG91ciBleHByZXNzaW9uIGRhdGEpIGFuZCBwdWxsIG91dCB0aGUgbWluIGFuZCBtYXggdG8gZGVmaW5lIG91ciBkYXRhIG1hcHBpbmcgcmFuZ2Ugb2YgdmFsdWVzLgoKKipOb3RlKio6IHlvdSBjb3VsZCBkZWZpbmUgdGhlIG1pbiBhbmQgbWF4IGJhc2VkIG9uIHRoZSBlbnRpcmUgZGF0YXNldCBvciBqdXN0IHRoZSBzdWJzZXQgdGhhdCBpcyByZXByZXNlbnRlZCBpbiBDeXRvc2NhcGUgY3VycmVudGx5LiAgVGhlIHR3byBtZXRob2RzIHdpbGwgZ2l2ZSB5b3UgZGlmZmVyZW50IHJlc3VsdHMuICBJZiB5b3UgaW50ZW5kIG9uIGNvbXBhcmluZyBkaWZmZXJlbnQgbmV0d29ya3MgY3JlYXRlZCB3aXRoIHRoZSBzYW1lIGRhdGFzZXQgdGhlbiBpdCBpcyBiZXN0IHRvIGNhbGN1bGF0ZSB0aGUgbWluIGFuZCBtYXggZnJvbSB0aGUgZW50aXJlIGRhdGFzZXQgYXMgb3Bwb3NlZCB0byBhIHN1YnNldC4gCgpgYGB7cn0KbWluLm1lc2VuLmxvZ2ZjID0gbWluKFJOQVNlcV9nZW5lX3Njb3JlcyRsb2dGQy5tZXNlbixuYS5ybT1UUlVFKQptYXgubWVzZW4ubG9nZmMgPSBtYXgoUk5BU2VxX2dlbmVfc2NvcmVzJGxvZ0ZDLm1lc2VuLG5hLnJtPVRSVUUpCmRhdGEudmFsdWVzID0gYyhtaW4ubWVzZW4ubG9nZmMsMCxtYXgubWVzZW4ubG9nZmMpCmBgYAoKCk5leHQsIHdlIHVzZSB0aGUgUkNvbG9yQnJld2VyIHBhY2thZ2UgdG8gaGVscCB1cyBwaWNrIGdvb2QgY29sb3JzIHRvIHBhaXIgd2l0aCBvdXIgZGF0YSB2YWx1ZXMuCmBgYHtyfQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKZGlzcGxheS5icmV3ZXIuYWxsKGxlbmd0aChkYXRhLnZhbHVlcyksIGNvbG9yYmxpbmRGcmllbmRseT1UUlVFLCB0eXBlPSJkaXYiKSAjIGRpdixxdWFsLHNlcSxhbGwKbm9kZS5jb2xvcnMgPC0gYyhyZXYoYnJld2VyLnBhbChsZW5ndGgoZGF0YS52YWx1ZXMpLCAiUmRCdSIpKSkKYGBgCgpNYXAgdGhlIGNvbG9ycyB0byBvdXIgZGF0YSB2YWx1ZSBhbmQgdXBkYXRlIG91ciB2aXN1YWwgc3R5bGUuIApgYGB7ciBldmFsPUZBTFNFfSAKc2V0Tm9kZUNvbG9yTWFwcGluZygibG9nRkMubWVzZW4iLCBkYXRhLnZhbHVlcywgbm9kZS5jb2xvcnMsIHN0eWxlLm5hbWU9c3R5bGUubmFtZSkKYGBgCgpSZW1lbWJlciwgU3RyaW5nIGluY2x1ZGVzIHlvdXIgcXVlcnkgcHJvdGVpbnMgYXMgd2VsbCBhcyBvdGhlciBwcm90ZWlucyB0aGF0IGFzc29jaWF0ZSB3aXRoIHlvdXIgcXVlcnkgcHJvdGVpbnMgKGluY2x1ZGluZyB0aGUgc3Ryb25nZXN0IGNvbm5lY3Rpb24gZmlyc3QpLiAgTm90IGFsbCBvZiB0aGUgcHJvdGVpbnMgaW4gdGhpcyBuZXR3b3JrIGFyZSB5b3VyIHRvcCBoaXRzLiAgSG93IGNhbiB3ZSB2aXN1YWxpemUgd2hpY2ggcHJvdGVpbnMgYXJlIG91ciB0b3AgTWVzZW5jaHltYWwgaGl0cz8KCkFkZCBhIGRpZmZlcmVudCBib3JkZXIgY29sb3Igb3IgY2hhbmdlIHRoZSBub2RlIHNoYXBlIGZvciBvdXIgdG9wIGhpdHMuCgpgYGB7ciBldmFsPUZBTFNFfQpnZXROb2RlU2hhcGVzKCkKCiNzZWxlY3QgdGhlIE5vZGVzIG9mIGludGVyZXN0CiNzZWxlY3ROb2RlKG5vZGVzID0gdG9wX21lc2VuY2h5bWFsX2dlbmVzJE5hbWUsIGJ5LmNvbD0iZGlzcGxheSBuYW1lIikKc2V0Tm9kZVNoYXBlQnlwYXNzKG5vZGUubmFtZXMgPSB0b3BfbWVzZW5jaHltYWxfZ2VuZXMkTmFtZSwgbmV3LnNoYXBlcyA9ICJUUklBTkdMRSIpCmBgYAoKQ2hhbmdlIHRoZSBzaXplIG9mIHRoZSBub2RlIHRvIGJlIGNvcnJlbGF0ZWQgd2l0aCB0aGUgTWVzZW5jaHltYWwgcC12YWx1ZS4gCgpgYGB7ciBldmFsPUZBTFNFfQpzZXROb2RlU2l6ZU1hcHBpbmcodGFibGUuY29sdW1uID0gJ0xSLm1lc2VuJywgCiAgICAgICAgICAgICAgICAgICB0YWJsZS5jb2x1bW4udmFsdWVzID0gYyhtaW4oUk5BU2VxX2dlbmVfc2NvcmVzJExSLm1lc2VuKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZWFuKFJOQVNlcV9nZW5lX3Njb3JlcyRMUi5tZXNlbiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4KFJOQVNlcV9nZW5lX3Njb3JlcyRMUi5tZXNlbikpLCAKICAgICAgICAgICAgICAgICAgIHNpemVzID0gYygzMCwgNjAsIDE1MCksbWFwcGluZy50eXBlID0gImMiLCBzdHlsZS5uYW1lID0gc3R5bGUubmFtZSkKYGBgCgpHZXQgYSBzY3JlZW5zaG90IG9mIHRoZSByZXN1bHRpbmcgbmV0d29yawpgYGB7ciBtZXNlbl9zdHJpbmdfbmV0d29ya19zY3JlZW5zaG90LCBpbmNsdWRlPVRSVUV9Cm1lc2VuX3N0cmluZ19uZXR3b3JrX3BuZ19maWxlX25hbWUgPC0gZmlsZS5wYXRoKGdldHdkKCksIjIzMF9Jc3Nlcmxpbl9SQ3kzX2ludHJvIiwgIm1lc2VuX3N0cmluZ19uZXR3b3JrLnBuZyIpCmBgYAoKYGBge3IgZXZhbD1GQUxTRX0KaWYoZmlsZS5leGlzdHMobWVzZW5fc3RyaW5nX25ldHdvcmtfcG5nX2ZpbGVfbmFtZSkpewogICNjeXRvc2NhcGUgaGFuZ3Mgd2FpdGluZyBmb3IgdXNlciByZXNwb25zZSBpZiBmaWxlIGFscmVhZHkgZXhpc3RzLiAgUmVtb3ZlIGl0IGZpcnN0CiAgcmVzcG9uc2U8LSBmaWxlLnJlbW92ZShtZXNlbl9zdHJpbmdfbmV0d29ya19wbmdfZmlsZV9uYW1lKQogIH0gCnJlc3BvbnNlIDwtIGV4cG9ydEltYWdlKG1lc2VuX3N0cmluZ19uZXR3b3JrX3BuZ19maWxlX25hbWUsIHR5cGUgPSAicG5nIikKYGBgCgpgYGB7ciBtZXNlbnN0cmluZ25ldHdvcmssIGVjaG89RkFMU0UsIGZpZy5jYXA9IkZvcm1hdHRlZCBTdHJpbmcgbmV0d29yayBmcm9tIG91ciBzZXQgb2YgTWVzZW5jaHltYWwgcXVlcnkgZ2VuZXMuICBBbm5vdGF0ZWQgd2l0aCBvdXIgZXhwcmVzc2luIGRhdGEifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhtZXNlbl9zdHJpbmdfbmV0d29ya19wbmdfZmlsZV9uYW1lKQpgYGAKCiMgVXNlIENhc2UgMiAtIFdoaWNoIGdlbmVzIGhhdmUgc2ltaWxhciBleHByZXNzaW9uLgoKSW5zdGVhZCBvZiBxdWVyeWluZyBleGlzdGluZyByZXNvdXJjZXMgbG9vayBmb3IgY29ycmVsYXRpb25zIGluIHlvdXIgb3duIGRhdGFzZXQgdG8gZmluZCBvdXQgd2hpY2ggZ2VuZXMgaGF2ZSBzaW1pbGFyIGV4cHJlc3Npb24uICBUaGVyZSBhcmUgbWFueSB0b29scyB0aGF0IGNhbiBhbmFseXplIHlvdXIgZGF0YSBmb3IgY29ycmVsYXRpb24uICBBIHBvcHVsYXIgdG9vbCBpcyBXZWlnaHRlZCBHZW5lIENvcnJlbGF0aW9uIE5ldHdvcmsgQW5hbHlzaXMgKFdHQ05BKVtAd2djbmFdIHdoaWNoIHRha2VzIGV4cHJlc3Npb24gZGF0YSBhbmQgY2FsY3VsYXRlcyBmdW5jdGlvbmFsIG1vZHVsZXMuICBBcyBhIHNpbXBsZSBleGFtcGxlIHdlIGNhbiB0cmFuc2Zvcm0gb3VyIGV4cHJlc3Npb24gZGF0YXNldCBpbnRvIGEgY29ycmVsYXRpb24gbWF0cml4LiAgCgpVc2luZyB0aGUgQ3l0b3NjYXBlIEFwcCwgYU1hdFJlYWRlcltAYW1hdHJlYWRlcl0sIHdlIHRyYW5zZm9ybSBvdXIgYWRqYWNlbmN5IG1hdHJpeCBpbnRvIGFuIGludGVyYWN0aW9uIG5ldHdvcmsuIEZpcnN0IHdlIGZpbHRlciB0aGUgY29ycmVsYXRpb24gbWF0cml4IHRvIGNvbnRhaW4gb25seSB0aGUgc3Ryb25nZXN0IGNvbm5lY3Rpb25zIChmb3IgZXhhbXBsZSwgb25seSBjb3JyZWxhdGlvbnMgZ3JlYXRlciB0aGFuIDAuOSkuIAoKYGBge3J9CgpSTkFTZXFfZXhwcmVzc2lvbiA8LSBSTkFTZXFfZXhwcmVzc2lvbl9tYXRyaXhbLDM6bmNvbChSTkFTZXFfZXhwcmVzc2lvbl9tYXRyaXgpXQoKcm93bmFtZXMoUk5BU2VxX2V4cHJlc3Npb24pIDwtIFJOQVNlcV9leHByZXNzaW9uX21hdHJpeCROYW1lClJOQXNlcV9jb3JyZWxhdGlvbl9tYXRyaXggPC0gY29yKHQoUk5BU2VxX2V4cHJlc3Npb24pLCBtZXRob2Q9InBlYXJzb24iKQoKI3NldCB0aGUgZGlhZ29uYWwgb2YgbWF0cml4IHRvIHplcm8gLSBlbGltaW5hdGUgc2VsZi1jb3JyZWxhdGlvbgpSTkFzZXFfY29ycmVsYXRpb25fbWF0cml4WyAKICByb3coUk5Bc2VxX2NvcnJlbGF0aW9uX21hdHJpeCkgPT0gY29sKFJOQXNlcV9jb3JyZWxhdGlvbl9tYXRyaXgpIF0gPC0gMAoKIyBzZXQgYWxsIGNvcnJlbGF0aW9ucyB0aGF0IGFyZSBsZXNzIHRoYW4gMC45IHRvIHplcm8KUk5Bc2VxX2NvcnJlbGF0aW9uX21hdHJpeFt3aGljaChSTkFzZXFfY29ycmVsYXRpb25fbWF0cml4PDAuOTApXSA8LSAwCgojZ2V0IHJpZCBvZiByb3dzIGFuZCBjb2x1bW5zIHRoYXQgaGF2ZSBubyBjb3JyZWxhdGlvbnMgd2l0aCB0aGUgYWJvdmUgdGhyZXNob2xkcwpSTkFzZXFfY29ycmVsYXRpb25fbWF0cml4IDwtIFJOQXNlcV9jb3JyZWxhdGlvbl9tYXRyaXhbd2hpY2gocm93U3VtcyhSTkFzZXFfY29ycmVsYXRpb25fbWF0cml4KSAhPSAwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICB3aGljaChjb2xTdW1zKFJOQXNlcV9jb3JyZWxhdGlvbl9tYXRyaXgpICE9MCldCgojd3JpdGUgb3V0IHRoZSBjb3JyZWxhdGlvbiBmaWxlCmNvcnJlbGF0aW9uX2ZpbGVuYW1lIDwtIGZpbGUucGF0aChnZXR3ZCgpLCAiMjMwX0lzc2VybGluX1JDeTNfaW50cm8iLCAiVENHQV9PVl9STkFzZXFfZXhwcmVzc2lvbl9jb3JyZWxhdGlvbl9tYXRyaXgudHh0IikgCndyaXRlLnRhYmxlKFJOQXNlcV9jb3JyZWxhdGlvbl9tYXRyaXgsICBmaWxlID0gY29ycmVsYXRpb25fZmlsZW5hbWUsIGNvbC5uYW1lcyAgPSBUUlVFLCByb3cubmFtZXMgPSBGQUxTRSwgc2VwID0gIlx0IiwgcXVvdGU9RkFMU0UpCgpgYGAKClVzZSB0aGUgQ3lSZXN0IGNhbGwgdG8gYWNjZXNzIHRoZSBhTWF0UmVhZGVyIGZ1bmN0aW9uYWxpdHkuCgpgYGB7ciBldmFsPUZBTFNFfQphbWF0X3VybCA8LSAiYU1hdFJlYWRlci92MS9pbXBvcnQiCmFtYXRfcGFyYW1zID0gbGlzdChmaWxlcyA9IGxpc3QoY29ycmVsYXRpb25fZmlsZW5hbWUpLAogICAgICAgICAgICAgICAgICAgZGVsaW1pdGVyID0gIlRBQiIsCiAgICAgICAgICAgICAgICAgICB1bmRpcmVjdGVkID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICBpZ25vcmVaZXJvcyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICBpbnRlcmFjdGlvbk5hbWUgPSAiY29ycmVsYXRlZCB3aXRoIiwKICAgICAgICAgICAgICAgICAgIHJvd05hbWVzID0gRkFMU0UKICAgICAgICAgICAgICAgICAgICkKIApyZXNwb25zZSA8LSBjeXJlc3RQT1NUKG9wZXJhdGlvbiA9IGFtYXRfdXJsLCBib2R5ID0gYW1hdF9wYXJhbXMsIGJhc2UudXJsID0gImh0dHA6Ly9sb2NhbGhvc3Q6MTIzNCIpCgpjdXJyZW50X25ldHdvcmtfaWQgPC0gcmVzcG9uc2UkZGF0YVsic3VpZCJdCmBgYAoKCmBgYHtyIGV2YWw9RkFMU0V9CiNyZWxheW91dCBuZXR3b3JrCmxheW91dE5ldHdvcmsoJ2Nvc2UnLAogICAgICAgICAgICAgIG5ldHdvcmsgPSBhcy5udW1lcmljKGN1cnJlbnRfbmV0d29ya19pZCkpCmBgYAoKCmBgYHtyIGV2YWw9RkFMU0V9CnJlbmFtZU5ldHdvcmsodGl0bGUgPSJDb2V4cHJlc3Npb25fbmV0d29ya19wZWFyMF85NV9uZXciLAogICAgICAgICAgICAgIG5ldHdvcmsgPSBhcy5udW1lcmljKGN1cnJlbnRfbmV0d29ya19pZCkpCmBgYAoKCk1vZGlmeSB0aGUgdmlzdWFsaXphdGlvbiB0byBzZWUgd2hlcmUgZWFjaCBnZW5lcyBpcyBwcmVkb21pbmFudGx5IGV4cHJlc3NlZC4gIExvb2sgYXQgdGhlIDQgZGlmZmVyZW50IHAtdmFsdWVzIGFzc29jaWF0ZWQgd2l0aCBlYWNoIGdlbmUgYW5kIGNvbG9yIHRoZSBub2RlcyB3aXRoIHRoZSB0eXBlIGFzc29jaWF0ZWQgd2l0aCB0aGUgbG93ZXN0IEZEUi4KCkxvYWQgaW4gdGhlIHNjb3JpbmcgZGF0YS4gIFNwZWNpZnkgdGhlIGNhbmNlciB0eXBlIHdoZXJlIHRoZSBnZW5lcyBoYXMgdGhlIGxvd2VzdCBGRFIgdmFsdWUuCgpgYGB7cn0Kbm9kZXNfaW5fbmV0d29yayA8LSByb3duYW1lcyhSTkFzZXFfY29ycmVsYXRpb25fbWF0cml4KQoKI2FkZCBhbiBhZGRpdGlvbmFsIGNvbHVtbiB0byB0aGUgZ2VuZSBzY29yZXMgdGFibGUgdG8gaW5kaWNhdGUgaW4gd2hpY2ggc2FtcGxlcwojIHRoZSBnZW5lIGlzIHNpZ25pZmljYW50Cm5vZGVfY2xhc3MgPC0gdmVjdG9yKGxlbmd0aCA9IGxlbmd0aChub2Rlc19pbl9uZXR3b3JrKSxtb2RlID0gImNoYXJhY3RlciIpCmZvcihpIGluIDE6bGVuZ3RoKG5vZGVzX2luX25ldHdvcmspKXsKICBjdXJyZW50X3JvdyA8LSB3aGljaChSTkFTZXFfZ2VuZV9zY29yZXMkTmFtZSA9PSBub2Rlc19pbl9uZXR3b3JrW2ldKQogIG1pbl9wdmFsdWUgPC0gbWluKFJOQVNlcV9nZW5lX3Njb3Jlc1tjdXJyZW50X3JvdywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JlcChjb2xuYW1lcyhSTkFTZXFfZ2VuZV9zY29yZXMpLCBwYXR0ZXJuID0gIkZEUiIpXSkKICBpZihSTkFTZXFfZ2VuZV9zY29yZXMkRkRSLm1lc2VuW2N1cnJlbnRfcm93XSA8PW1pbl9wdmFsdWUpewogICAgbm9kZV9jbGFzc1tpXSA8LSBwYXN0ZShub2RlX2NsYXNzW2ldLCJtZXNlbiIsc2VwID0gIiAiKQogIH0KICBpZihSTkFTZXFfZ2VuZV9zY29yZXMkRkRSLmRpZmZbY3VycmVudF9yb3ddIDw9bWluX3B2YWx1ZSl7CiAgICBub2RlX2NsYXNzW2ldIDwtIHBhc3RlKG5vZGVfY2xhc3NbaV0sImRpZmYiLHNlcCA9ICIgIikKICB9CiAgaWYoUk5BU2VxX2dlbmVfc2NvcmVzJEZEUi5wcm9saWZbY3VycmVudF9yb3ddIDw9bWluX3B2YWx1ZSl7CiAgICBub2RlX2NsYXNzW2ldIDwtIHBhc3RlKG5vZGVfY2xhc3NbaV0sInByb2xpZiIsc2VwID0gIiAiKQogIH0KICBpZihSTkFTZXFfZ2VuZV9zY29yZXMkRkRSLmltbXVub1tjdXJyZW50X3Jvd10gPD1taW5fcHZhbHVlKXsKICAgIG5vZGVfY2xhc3NbaV0gPC0gcGFzdGUobm9kZV9jbGFzc1tpXSwiaW1tdW5vIixzZXAgPSAiICIpCiAgfQp9Cm5vZGVfY2xhc3MgPC0gdHJpbXdzKG5vZGVfY2xhc3MpCm5vZGVfY2xhc3NfZGYgPC1kYXRhLmZyYW1lKG5hbWU9bm9kZXNfaW5fbmV0d29yaywgbm9kZV9jbGFzcyxzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCgpoZWFkKG5vZGVfY2xhc3NfZGYpCmBgYAoKTWFwIHRoZSBuZXcgbm9kZSBhdHRyaWJ1dGUgYW5kIHRoZSBhbGwgdGhlIGdlbmUgc2NvcmVzIHRvIHRoZSBuZXR3b3JrLgpgYGB7ciBldmFsPUZBTFNFfQpsb2FkVGFibGVEYXRhKFJOQVNlcV9nZW5lX3Njb3Jlcyx0YWJsZS5rZXkuY29sdW1uID0gIm5hbWUiLGRhdGEua2V5LmNvbHVtbiA9ICJOYW1lIikgICNkZWZhdWx0IGRhdGEuZnJhbWUga2V5IGlzIHJvdy5uYW1lcwoKbG9hZFRhYmxlRGF0YShub2RlX2NsYXNzX2RmLHRhYmxlLmtleS5jb2x1bW4gPSAibmFtZSIsZGF0YS5rZXkuY29sdW1uID0gIm5hbWUiKSAgI2RlZmF1bHQgZGF0YS5mcmFtZSBrZXkgaXMgcm93Lm5hbWVzCmBgYAoKQ3JlYXRlIGEgY29sb3IgbWFwcGluZyBmb3IgdGhlIGRpZmZlcmVudCBjYW5jZXIgdHlwZXMuCgpgYGB7ciBldmFsPUZBTFNFfQojY3JlYXRlIGEgbmV3IG1hcHBpbmcgd2l0aCB0aGUgZGlmZmVyZW50IHR5cGVzCnVuaXF1ZV90eXBlcyA8LSBzb3J0KHVuaXF1ZShub2RlX2NsYXNzKSkKCmNvdWwgPSBicmV3ZXIucGFsKDQsICJTZXQxIikgCiAKIyBJIGNhbiBhZGQgbW9yZSB0b25lcyB0byB0aGlzIHBhbGV0dGUgOgpjb3VsID0gY29sb3JSYW1wUGFsZXR0ZShjb3VsKShsZW5ndGgodW5pcXVlX3R5cGVzKSkKCnNldE5vZGVDb2xvck1hcHBpbmcodGFibGUuY29sdW1uID0gIm5vZGVfY2xhc3MiLHRhYmxlLmNvbHVtbi52YWx1ZXMgPSB1bmlxdWVfdHlwZXMsCiAgICAgICAgICAgICAgICAgICAgY29sb3JzID0gY291bCxtYXBwaW5nLnR5cGUgPSAiZCIpCmBgYAoKYGBge3IgY29ycmVsYXRpb25uZXR3b3JrLCBpbmNsdWRlPVRSVUV9CmNvcnJlbGF0aW9uX25ldHdvcmtfcG5nX2ZpbGVfbmFtZSA8LSBmaWxlLnBhdGgoZ2V0d2QoKSwiMjMwX0lzc2VybGluX1JDeTNfaW50cm8iLCAiY29ycmVsYXRpb25fbmV0d29yay5wbmciKQoKYGBgCgpgYGB7ciBldmFsPUZBTFNFfQppZihmaWxlLmV4aXN0cyhjb3JyZWxhdGlvbl9uZXR3b3JrX3BuZ19maWxlX25hbWUpKXsKICAjY3l0b3NjYXBlIGhhbmdzIHdhaXRpbmcgZm9yIHVzZXIgcmVzcG9uc2UgaWYgZmlsZSBhbHJlYWR5IGV4aXN0cy4gIFJlbW92ZSBpdCBmaXJzdAogIGZpbGUucmVtb3ZlKGNvcnJlbGF0aW9uX25ldHdvcmtfcG5nX2ZpbGVfbmFtZSkKICB9IAoKI2V4cG9ydCB0aGUgbmV0d29yawpleHBvcnRJbWFnZShjb3JyZWxhdGlvbl9uZXR3b3JrX3BuZ19maWxlX25hbWUsIHR5cGUgPSAicG5nIikKYGBgCgpgYGB7ciBjb3JyZWxhdGlvbk5ldHdvcmsyLCBlY2hvPUZBTFNFLCBmaWcuY2FwPSJFeGFtcGxlIGNvcnJlbGF0aW9uIG5ldHdvcmsgY3JlYXRlZCB1c2luZyBhTWF0UmVhZGVyIn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoY29ycmVsYXRpb25fbmV0d29ya19wbmdfZmlsZV9uYW1lKQpgYGAKCmNsdXN0ZXIgdGhlIE5ldHdvcmsKYGBge3IgZXZhbD1GQUxTRX0KI21ha2Ugc3VyZSBpdCBpcyBzZXQgdG8gdGhlIHJpZ2h0IG5ldHdvcmsKICBzZXRDdXJyZW50TmV0d29yayhuZXR3b3JrID0gZ2V0TmV0d29ya05hbWUoc3VpZD1hcy5udW1lcmljKGN1cnJlbnRfbmV0d29ya19pZCkpKQoKICAjY2x1c3RlciB0aGUgbmV0d29yawogIGNsdXN0ZXJtYWtlcl91cmwgPC0gcGFzdGUoImNsdXN0ZXIgbWNsIG5ldHdvcms9U1VJRDoiLGN1cnJlbnRfbmV0d29ya19pZCwgc2VwPSIiKQogIGNvbW1hbmRzR0VUKGNsdXN0ZXJtYWtlcl91cmwpCiAgCiAgI2dldCB0aGUgY2x1c3RlcmluZyByZXN1bHRzCiAgZGVmYXVsdF9ub2RlX3RhYmxlIDwtIGdldFRhYmxlQ29sdW1ucyh0YWJsZT0gIm5vZGUiLG5ldHdvcmsgPSBhcy5udW1lcmljKGN1cnJlbnRfbmV0d29ya19pZCkpCiAKICBoZWFkKGRlZmF1bHRfbm9kZV90YWJsZSkKYGBgCgpQZXJmb3JtIHBhdGh3YXkgRW5yaWNobWVudCBvbiBvbmUgb2YgdGhlIGNsdXN0ZXJzIHVzaW5nIGc6UHJvZmlsZXJbQGdwcm9maWxlcl0uIGc6UHJvZmlsZXIgaXMgYW4gb25saW5lIGZ1bmN0aW9uYWwgZW5yaWNobWVudCB3ZWIgc2VydmljZSB0aGF0IHdpbGwgdGFrZSB5b3VyIGdlbmUgbGlzdCBhbmQgcmV0dXJuIHRoZSBzZXQgb2YgZW5yaWNoZWQgcGF0aHdheXMuICBGb3IgYXV0b21hdGVkIGFuYWx5c2lzIGc6UHJvZmlsZXIgaGFzIGNyZWF0ZWQgYW4gUiBsaWJyYXJ5IHRvIGludGVyYWN0IHdpdGggaXQgZGlyZWN0bHkgZnJvbSBSIGluc3RlYWQgb2YgdXNpbmcgdGhlIHdlYiBwYWdlLiAKCkNyZWF0ZSBhIGZ1bmN0aW9uIHRvIGNhbGwgZzpQcm9maWxlciBhbmQgY29udmVydCB0aGUgcmV0dXJuZWQgcmVzdWx0cyBpbnRvIGEgZ2VuZXJpYyBlbnJpY2htZW50IG1hcCBpbnB1dCBmaWxlLgpgYGB7cn0KdHJ5Q2F0Y2goZXhwciA9IHsgbGlicmFyeSgiZ1Byb2ZpbGVSIil9LCAKICAgICAgICAgZXJyb3IgPSBmdW5jdGlvbihlKSB7IGluc3RhbGwucGFja2FnZXMoImdQcm9maWxlUiIpfSwgZmluYWxseSA9IGxpYnJhcnkoImdQcm9maWxlUiIpKQoKI2Z1bmN0aW9uIHRvIHJ1biBncHJvZmlsZXIgdXNpbmcgdGhlIGdwcm9maWxlciBsaWJyYXJ5CiMgCiMgVGhlIGZ1bmN0aW9uIHRha2VzIHRoZSByZXR1cm5lZCBncHJvZmlsZXIgcmVzdWx0cyBhbmQgZm9ybWF0cyBpdCB0byB0aGUgZ2VuZXJpYyBFTSBpbnB1dCBmaWxlCiMKIyBmdW5jdGlvbiByZXR1cm5zIGEgZGF0YSBmcmFtZSBpbiB0aGUgZ2VuZXJpYyBFTSBmaWxlIGZvcm1hdC4KcnVuR3Byb2ZpbGVyIDwtIGZ1bmN0aW9uKGdlbmVzLGN1cnJlbnRfb3JnYW5pc20gPSAiaHNhcGllbnMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgIHNpZ25pZmljYW50X29ubHkgPSBGLCBzZXRfc2l6ZV9tYXggPSAyMDAsIAogICAgICAgICAgICAgICAgICAgICAgICAgc2V0X3NpemVfbWluID0gMywgZmlsdGVyX2dzX3NpemVfbWluID0gNSAsIGV4Y2x1ZGVfaWVhID0gRil7CiAgCiAgZ3Byb2ZpbGVyX3Jlc3VsdHMgPC0gZ3Byb2ZpbGVyKGdlbmVzICwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2lnbmlmaWNhbnQ9c2lnbmlmaWNhbnRfb25seSxvcmRlcmVkX3F1ZXJ5ID0gRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleGNsdWRlX2llYT1leGNsdWRlX2llYSxtYXhfc2V0X3NpemUgPSBzZXRfc2l6ZV9tYXgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbl9zZXRfc2l6ZSA9IHNldF9zaXplX21pbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29ycmVjdGlvbl9tZXRob2QgPSAiZmRyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JnYW5pc20gPSBjdXJyZW50X29yZ2FuaXNtLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNyY19maWx0ZXIgPSBjKCJHTzpCUCIsIlJFQUMiKSkKICAKICAjZmlsdGVyIHJlc3VsdHMKICBncHJvZmlsZXJfcmVzdWx0cyA8LSBncHJvZmlsZXJfcmVzdWx0c1t3aGljaChncHJvZmlsZXJfcmVzdWx0c1ssJ3Rlcm0uc2l6ZSddID49IDMKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICYgZ3Byb2ZpbGVyX3Jlc3VsdHNbLCdvdmVybGFwLnNpemUnXSA+PSBmaWx0ZXJfZ3Nfc2l6ZV9taW4gKSxdCiAgCiAgIyBnUHJvZmlsZVIgcmV0dXJucyBjb3JyZWN0ZWQgcC12YWx1ZXMgb25seS4gIFNldCBwLXZhbHVlIHRvIGNvcnJlY3RlZCBwLXZhbHVlCiAgaWYoZGltKGdwcm9maWxlcl9yZXN1bHRzKVsxXSA+IDApewogICAgZW1fcmVzdWx0cyA8LSBjYmluZChncHJvZmlsZXJfcmVzdWx0c1ssCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygidGVybS5pZCIsInRlcm0ubmFtZSIsInAudmFsdWUiLCJwLnZhbHVlIildLCAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdwcm9maWxlcl9yZXN1bHRzWywiaW50ZXJzZWN0aW9uIl0pCiAgY29sbmFtZXMoZW1fcmVzdWx0cykgPC0gYygiTmFtZSIsIkRlc2NyaXB0aW9uIiwgInB2YWx1ZSIsInF2YWx1ZSIsInBoZW5vdHlwZSIsImdlbmVzIikKICAKICByZXR1cm4oZW1fcmVzdWx0cykKICB9IGVsc2UgewogICAgcmV0dXJuKCJubyBncHJvZmlsZXIgcmVzdWx0cyBmb3Igc3VwcGxpZWQgcXVlcnkiKQogIH0KfQpgYGAKClJ1biBnOlByb2ZpbGVyLiAgZzpQcm9maWxlciB3aWxsIHJldHVybiBhIHNldCBvZiBwYXRod2F5cyBhbmQgZnVuY3Rpb25zIHRoYXQgYXJlIGZvdW5kIHRvIGJlIGVucmljaGVkIGluIG91ciBxdWVyeSBzZXQgb2YgZ2VuZXMuICAKCmBgYHtyIGV2YWw9RkFMU0V9CiAgY3VycmVudF9jbHVzdGVyIDwtICIxIgogICNzZWxlY3QgYWxsIHRoZSBub2RlcyBpbiBjbHVzdGVyIDEKICBzZWxlY3RlZG5vZGVzIDwtIHNlbGVjdE5vZGVzKGN1cnJlbnRfY2x1c3RlciwgYnkuY29sPSJfX21jbENsdXN0ZXIiKQogIAogICNjcmVhdGUgYSBzdWJuZXR3b3JrIHdpdGggY2x1c3RlciAxCiAgc3VibmV0d29ya19zdWlkIDwtIGNyZWF0ZVN1Ym5ldHdvcmsobm9kZXM9InNlbGVjdGVkIikKICAKICByZW5hbWVOZXR3b3JrKCJDbHVzdGVyMV9TdWJuZXR3b3JrIiwgbmV0d29yaz1hcy5udW1lcmljKHN1Ym5ldHdvcmtfc3VpZCkpCiAgCiAgc3VibmV0d29ya19ub2RlX3RhYmxlIDwtIGdldFRhYmxlQ29sdW1ucyh0YWJsZT0gIm5vZGUiLG5ldHdvcmsgPSBhcy5udW1lcmljKHN1Ym5ldHdvcmtfc3VpZCkpCgogIGVtX3Jlc3VsdHMgPC0gcnVuR3Byb2ZpbGVyKHN1Ym5ldHdvcmtfbm9kZV90YWJsZSRuYW1lKQogIAogI3dyaXRlIG91dCB0aGUgZzpQcm9maWxlciByZXN1bHRzCiBlbV9yZXN1bHRzX2ZpbGVuYW1lIDwtZmlsZS5wYXRoKGdldHdkKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMjMwX0lzc2VybGluX1JDeTNfaW50cm8iLHBhc3RlKCJncHJvZmlsZXJfY2x1c3RlciIsY3VycmVudF9jbHVzdGVyLCJlbnJfcmVzdWx0cy50eHQiLHNlcD0iXyIpKQoKICB3cml0ZS50YWJsZShlbV9yZXN1bHRzLGVtX3Jlc3VsdHNfZmlsZW5hbWUsY29sLm5hbWU9VFJVRSxzZXA9Ilx0Iixyb3cubmFtZXM9RkFMU0UscXVvdGU9RkFMU0UpCiAgCiAKICBoZWFkKGVtX3Jlc3VsdHMpCmBgYAoKQ3JlYXRlIGFuIGVucmljaG1lbnQgbWFwIHdpdGggdGhlIHJldHVybmVkIGc6UHJvZmlsZXIgcmVzdWx0cy4gIEFuIGVucmljaG1lbnQgbWFwIGlzIGEgZGlmZmVyZW50IHNvcnQgb2YgbmV0d29yay4gIEluc3RlYWQgb2Ygbm9kZXMgcmVwcmVzZW50aW5nIGdlbmVzLCBub2RlcyByZXByZXNlbnQgcGF0aHdheXMgb3IgZnVuY3Rpb25zLiAgRWRnZXMgYmV0d2VlbiB0aGVzZSBwYXRod2F5cyBvciBmdW5jdGlvbnMgcmVwcmVzZW50IHNoYXJlZCBnZW5lcyBvciBwYXRod2F5IGNyb3NzdGFsay4gIEFuIGVucmljaG1lbnQgbWFwIGlzIGEgd2F5IHRvIHZpc3VhbGl6ZSB5b3VyIGVucmljaG1lbnQgcmVzdWx0cyB0byBoZWxwIHJlZHVjZSByZWR1bmRhbmN5IGFuZCB1bmNvdmVyIG1haW4gdGhlbWVzLiAgUGF0aHdheXMgY2FuIGFsc28gYmUgZXhwbG9yZWQgaW4gZGV0YWlsIHVzaW5nIHRoZSBmZWF0dXJlcyBhdmFpbGFibGUgdGhyb3VnaCB0aGUgQXBwIGluIEN5dG9zY2FwZS4gCmBgYHtyIGV2YWw9RkFMU0V9CiBlbV9jb21tYW5kID0gcGFzdGUoJ2VucmljaG1lbnRtYXAgYnVpbGQgYW5hbHlzaXNUeXBlPSJnZW5lcmljIiAnLCAKICAgICAgICAgICAgICAgICAgICdwdmFsdWU9JywiMC4wNSIsICdxdmFsdWU9JywiMC4wNSIsCiAgICAgICAgICAgICAgICAgICAnc2ltaWxhcml0eWN1dG9mZj0nLCIwLjI1IiwKICAgICAgICAgICAgICAgICAgICdjb2VmZmVjaWVudHM9JywiSkFDQ0FSRCIsCiAgICAgICAgICAgICAgICAgICAnZW5yaWNobWVudHNEYXRhc2V0MT0nLGVtX3Jlc3VsdHNfZmlsZW5hbWUgLAogICAgICAgICAgICAgICAgICAgc2VwPSIgIikKCiAgI2VucmljaG1lbnQgbWFwIGNvbW1hbmQgd2lsbCByZXR1cm4gdGhlIHN1aWQgb2YgbmV3bHkgY3JlYXRlZCBuZXR3b3JrLgogIGVtX25ldHdvcmtfc3VpZCA8LSBjb21tYW5kc1J1bihlbV9jb21tYW5kKQogIAogIHJlbmFtZU5ldHdvcmsoIkNsdXN0ZXIxX2VucmljaG1lbnRtYXAiLCBuZXR3b3JrPWFzLm51bWVyaWMoZW1fbmV0d29ya19zdWlkKSkKYGBgCgpFeHBvcnQgaW1hZ2Ugb2YgcmVzdWx0aW5nIEVucmljaG1lbnQgbWFwLgoKYGBge3IgY2x1c3RlcjFlbSwgaW5jbHVkZT1UUlVFfQpjbHVzdGVyMWVtX3BuZ19maWxlX25hbWUgPC0gZmlsZS5wYXRoKGdldHdkKCksIjIzMF9Jc3Nlcmxpbl9SQ3kzX2ludHJvIiwiY2x1c3RlcjFlbS5wbmciKQoKYGBgCgpgYGB7ciBldmFsPUZBTFNFfQppZihmaWxlLmV4aXN0cyhjbHVzdGVyMWVtX3BuZ19maWxlX25hbWUpKXsKICAjY3l0b3NjYXBlIGhhbmdzIHdhaXRpbmcgZm9yIHVzZXIgcmVzcG9uc2UgaWYgZmlsZSBhbHJlYWR5IGV4aXN0cy4gIFJlbW92ZSBpdCBmaXJzdAogIGZpbGUucmVtb3ZlKGNsdXN0ZXIxZW1fcG5nX2ZpbGVfbmFtZSkKICB9IAoKI2V4cG9ydCB0aGUgbmV0d29yawpleHBvcnRJbWFnZShjbHVzdGVyMWVtX3BuZ19maWxlX25hbWUsIHR5cGUgPSAicG5nIikKYGBgCgpgYGB7ciBjbHVzdGVyMWVtZmlnLCBlY2hvPUZBTFNFLCBmaWcuY2FwPSJFeGFtcGxlIEVucmljaG1lbnQgTWFwIGNyZWF0ZWQgd2hlbiBydW5uaW5nIGFuIGVucmljaG1lbnQgYW5hbHlzaXMgdXNpbmcgZzpQcm9maWxlciB3aXRoIHRoZSBnZW5lcyB0aGF0IGFyZSBwYXJ0IG9mIGNsdXN0ZXIgMSJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGNsdXN0ZXIxZW1fcG5nX2ZpbGVfbmFtZSkKYGBgCgpBbm5vdGF0ZSB0aGUgRW5yaWNobWVudCBtYXAgdG8gZ2V0IHRoZSBnZW5lcmFsIHRoZW1lcyB0aGF0IGFyZSBmb3VuZCBpbiB0aGUgZW5yaWNobWVudCByZXN1bHRzIG9mIGNsdXN0ZXIgMQpgYGB7ciBldmFsPUZBTFNFfQoKI2dldCB0aGUgY29sdW1uIGZyb20gdGhlIG5vZGV0YWJsZSBhbmQgbm9kZSB0YWJsZQogIG5vZGV0YWJsZV9jb2xuYW1lcyA8LSBnZXRUYWJsZUNvbHVtbk5hbWVzKHRhYmxlPSJub2RlIiwgIG5ldHdvcmsgPSAgYXMubnVtZXJpYyhlbV9uZXR3b3JrX3N1aWQpKQoKICBkZXNjcl9hdHRyaWIgPC0gbm9kZXRhYmxlX2NvbG5hbWVzW2dyZXAobm9kZXRhYmxlX2NvbG5hbWVzLCBwYXR0ZXJuID0gIkdTX0RFU0NSIildCgogICNBdXRvYW5ub3RhdGUgdGhlIG5ldHdvcmsKICBhdXRvYW5ub3RhdGVfdXJsIDwtIHBhc3RlKCJhdXRvYW5ub3RhdGUgYW5ub3RhdGUtY2x1c3RlckJvb3N0ZWQgbGFiZWxDb2x1bW49IiwgZGVzY3JfYXR0cmliLCIgbWF4V29yZHM9MyAiLCBzZXA9IiIpCiAgICBjdXJyZW50X25hbWUgPC1jb21tYW5kc0dFVChhdXRvYW5ub3RhdGVfdXJsKQoKYGBgCgpFeHBvcnQgaW1hZ2Ugb2YgcmVzdWx0aW5nIEFubm90YXRlZCBFbnJpY2htZW50IG1hcC4KCmBgYHtyIGNsdXN0ZXIxZW1hbm5vdCwgaW5jbHVkZT1UUlVFfQpjbHVzdGVyMWVtX2Fubm90X3BuZ19maWxlX25hbWUgPC0gZmlsZS5wYXRoKGdldHdkKCksICIyMzBfSXNzZXJsaW5fUkN5M19pbnRybyIsImNsdXN0ZXIxZW1fYW5ub3QucG5nIikKYGBgCgpgYGB7ciBldmFsPUZBTFNFfQppZihmaWxlLmV4aXN0cyhjbHVzdGVyMWVtX2Fubm90X3BuZ19maWxlX25hbWUpKXsKICAjY3l0b3NjYXBlIGhhbmdzIHdhaXRpbmcgZm9yIHVzZXIgcmVzcG9uc2UgaWYgZmlsZSBhbHJlYWR5IGV4aXN0cy4gIFJlbW92ZSBpdCBmaXJzdAogIGZpbGUucmVtb3ZlKGNsdXN0ZXIxZW1fYW5ub3RfcG5nX2ZpbGVfbmFtZSkKICB9IAoKI2V4cG9ydCB0aGUgbmV0d29yawpleHBvcnRJbWFnZShjbHVzdGVyMWVtX2Fubm90X3BuZ19maWxlX25hbWUsIHR5cGUgPSAicG5nIikKYGBgCgpgYGB7ciBjbHVzdGVyMWVtYW5ub3RmaWcsIGVjaG89RkFMU0UsIGZpZy5jYXA9IkV4YW1wbGUgQW5ub3RhdGVkIEVucmljaG1lbnQgTWFwIGNyZWF0ZWQgd2hlbiBydW5uaW5nIGFuIGVucmljaG1lbnQgYW5hbHlzaXMgdXNpbmcgZzpQcm9maWxlciB3aXRoIHRoZSBnZW5lcyB0aGF0IGFyZSBwYXJ0IG9mIGNsdXN0ZXIgMSJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGNsdXN0ZXIxZW1fYW5ub3RfcG5nX2ZpbGVfbmFtZSkKYGBgCgpEZW5zZSBuZXR3b3JrcyBzbWFsbCBvciBsYXJnZSBuZXZlciBsb29rIGxpa2UgbmV0d29yayBmaWd1cmVzIHdlIHNvIG9mdGVuIHNlZSBpbiBqb3VybmFscy4gIEEgbG90IG9mIG1hbnVhbCB0d2Vha2luZywgcmVvcmdhbml6YXRpb24gYW5kIG9wdGltaXphdGlvbiBpcyBpbnZvbHZlZCBpbiBnZXR0aW5nIHRoYXQgcGVyZmVjdCBmaWd1cmUgcmVhZHkgbmV0d29yay4gIFRoZSBhYm92ZSBuZXR3b3JrIGlzIHdoYXQgdGhlIG5ldHdvcmsgc3RhcnRzIGFzLiAgVGhlIGJlbG93IGZpZ3VyZSBpcyB3aGF0IGl0IGNhbiBsb29rIGxpa2UgYWZ0ZXIgYSBmZXcgbWludXRlcyBvZiBtYW51YWwgcmVvcmdhbmlhemF0aW9uLiAgKGluZGl2aWR1YWwgY2x1c3RlcnMgd2VyZSBzZWxlY3RlZCBmcm9tIHRoZSBhdXRvIGFubm90YXRlIHBhbmVsIGFuZCBzZXBhcmF0ZWQgZnJvbSBvdGhlciBjbHVzdGVycykKCjxjZW50ZXI+CiFbXShodHRwczovL2N5dG9zY2FwZS5naXRodWIuaW8vY3l0b3NjYXBlLWF1dG9tYXRpb24vZm9yLXNjcmlwdGVycy9SL25vdGVib29rcy9iaW9jMjAxOF9SY3kzX2ludHJvLzIzMF9Jc3Nlcmxpbl9SQ3kzX2ludHJvL2ltYWdlcy9jbHVzdGVyMWVtX2Fubm90X21hbi5wbmcpCjwvY2VudGVyPg==